在Java中如何使用transient

Java語言的transient不像class、synchronized和其餘熟悉的關鍵字那樣衆所周知,於是它會出如今一些面試題中。這篇文章我將爲你們講解transient。java

transient的用途

Q:transient關鍵字能實現什麼?node

A:當對象被序列化時(寫入字節序列到目標文件)時,transient阻止實例中那些用此關鍵字聲明的變量持久化;當對象被反序列化時(從源文件讀取字節序列進行重構),這樣的實例變量值不會被持久化和恢復。例如,當反序列化對象——數據流(例如,文件)可能不存在時,緣由是你的對象中存在類型爲java.io.InputStream的變量,序列化時這些變量引用的輸入流沒法被打開。面試

transient使用介紹

Q:如何使用transient?this

A:包含實例變量聲明中的transient修飾符。片斷1提供了小的演示。spa

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
 
class ClassLib implements Serializable {
     private transient InputStream is;
 
     private int majorVer;
     private int minorVer;
 
     ClassLib(InputStream is) throws IOException {
         System.out.println( "ClassLib(InputStream) called" );
         this .is = is;
         DataInputStream dis;
         if (is instanceof DataInputStream)
             dis = (DataInputStream) is;
         else
             dis = new DataInputStream(is);
         if (dis.readInt() != 0xcafebabe )
             throw new IOException( "not a .class file" );
         minorVer = dis.readShort();
         majorVer = dis.readShort();
     }
 
     int getMajorVer() {
         return majorVer;
     }
 
     int getMinorVer() {
         return minorVer;
     }
 
     void showIS() {
         System.out.println(is);
     }
}
 
public class TransDemo {
     public static void main(String[] args) throws IOException {
         if (args.length != 1 ) {
             System.err.println( "usage: java TransDemo classfile" );
             return ;
         }
         ClassLib cl = new ClassLib( new FileInputStream(args[ 0 ]));
         System.out.printf( "Minor version number: %d%n" , cl.getMinorVer());
         System.out.printf( "Major version number: %d%n" , cl.getMajorVer());
         cl.showIS();
 
         try (FileOutputStream fos = new FileOutputStream( "x.ser" );
                 ObjectOutputStream oos = new ObjectOutputStream(fos)) {
             oos.writeObject(cl);
         }
 
         cl = null ;
 
         try (FileInputStream fis = new FileInputStream( "x.ser" );
                 ObjectInputStream ois = new ObjectInputStream(fis)) {
             System.out.println();
             cl = (ClassLib) ois.readObject();
             System.out.printf( "Minor version number: %d%n" , cl.getMinorVer());
             System.out.printf( "Major version number: %d%n" , cl.getMajorVer());
             cl.showIS();
         } catch (ClassNotFoundException cnfe) {
             System.err.println(cnfe.getMessage());
         }
     }
}

片斷1:序列化和反序列化ClassLib對象code

片斷1中聲明ClassLib和TransDemo類。ClassLib是一個讀取Java類文件的庫,而且實現了 java.io.Serializable接口,從而這些實例能被序列化和反序列化。TransDemo是一個用來序列化和反序列化ClassLib實例 的應用類。對象

ClassLib聲明它的實例變量爲transient,緣由是它能夠毫無心義的序列化一個輸入流(像上面講述的那樣)。事實上,若是此變量不是 transient的話,當反序列化x.ser的內容時,則會拋出java.io.NotSerializableException,緣由是 InputStream沒有實現Serializable接口。接口

編譯片斷1:javac TransDemo.java;帶一個參數TransDemo.class運行應用:java TransDemo TransDemo.class。你或許會看到相似下面的輸出:ci

1
2
3
4
5
6
7
8
ClassLib(InputStream) called
Minor version number: 0
Major version number: 51
java.io.FileInputStream@79f1e0e0
 
Minor version number: 0
Major version number: 51
null

以上輸出代表:當對象被重構時,沒有構造方法調用。此外,is假定默認爲null,相比較,當ClassLib對象序列化時,majorVer和minorVer是有值的。get

類中的成員變量和transient

Q:類中的成員變量中可使用transient嗎?

A:問題答案請看片斷2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class TransDemo {
     public static void main(String[] args) throws IOException {
         Foo foo = new Foo();
         System.out.printf( "w: %d%n" , Foo.w);
         System.out.printf( "x: %d%n" , Foo.x);
         System.out.printf( "y: %d%n" , foo.y);
         System.out.printf( "z: %d%n" , foo.z);
         try (FileOutputStream fos = new FileOutputStream( "x.ser" );
                 ObjectOutputStream oos = new ObjectOutputStream(fos)) {
             oos.writeObject(foo);
         }
 
         foo = null ;
 
         try (FileInputStream fis = new FileInputStream( "x.ser" );
                 ObjectInputStream ois = new ObjectInputStream(fis)) {
             System.out.println();
             foo = (Foo) ois.readObject();
             System.out.printf( "w: %d%n" , Foo.w);
             System.out.printf( "x: %d%n" , Foo.x);
             System.out.printf( "y: %d%n" , foo.y);
             System.out.printf( "z: %d%n" , foo.z);
         } catch (ClassNotFoundException cnfe) {
             System.err.println(cnfe.getMessage());
         }
     }
}

片斷2:序列化和反序列化Foo對象

片斷2有點相似片斷1。但不一樣的是,序列化和反序列化的是Foo對象,而不是ClassLib。此外,Foo包含一對變量,w和x,以及實例變量y和z。

編譯片斷2(javac TransDemo.java)並運行應用(java TransDemo)。你能夠看到以下輸出:

1
2
3
4
5
6
7
8
9
w: 1
x: 2
y: 3
z: 4
 
w: 1
x: 2
y: 3
z: 0

這個輸出告訴咱們,實例變量y是被序列化的,z卻沒有,它被標記transient。可是,當Foo被序列化時,它並無告訴咱們,是否變量w和x被序列化和反序列化,是否只是以普通類初始化方式初始。對於答案,咱們須要查看x.ser的內容。

下面顯示x.ser十六進制:

1
2
00000000 AC ED 00 05 73 72 00 03 46 6F 6F FC 7A 5D 82 1D ....sr..Foo.z]..
00000010 D2 9D 3F 02 00 01 49 00 01 79 78 70 00 00 00 03 ..?...I..yxp....

因爲JavaWorld中的「The Java serialization algorithm revealed」這篇文章,咱們發現輸出的含義:

  • AC ED 序列化協議標識

  • 00 05 流版本號

  • 73 表示這是一個新對象

  • 72 表示這是一個新的類

  • 00 03 表示類名長度(3)

  • 46 6F 6F 表示類名(Foo)

  • FC 7A 5D 82 1D D2 9D 3F 表示類的串行版本標識符

  • 02 表示該對象支持序列化

  • 00 01 表示這個類的變量數量(1)

  • 49 變量類型代碼 (0×49, 或I, 表示int)

  • 00 01 表示變量名長度(1)

  • 79 變量名稱(y)

  • 78 表示該對象可選的數據塊末端

  • 70 表示咱們已經到達類層次結構的頂部

  • 00 00 00 03 表示y的值(3)

顯而易見,只有實例變量y被序列化。由於z是transient,因此不能序列化。此外,即便它們標記transien,w和x不能被序列化,緣由是它們類變量不能序列化。

相關文章
相關標籤/搜索