Java語言的transient不像class、synchronized和其他熟悉的關鍵字那樣衆所周知,因而它會出現在一些面試題中。這篇文章我將爲大家講解transient。
transient的用途
Q:transient關鍵字能實現什麼?
A:當對象被序列化時(寫入字節序列到目標文件)時,transient阻止實例中那些用此關鍵字聲明的變量持久化;當對象被反序列化時(從源文件讀取字節序列進行重構),這樣的實例變量值不會被持久化和恢復。例如,當反序列化對象——數據流(例如,文件)可能不存在時,原因是你的對象中存在類型爲java.io.InputStream的變量,序列化時這些變量引用的輸入流無法被打開。
transient使用介紹
Q:如何使用transient?
A:包含實例變量聲明中的transient修飾符。片段1提供了小的演示。
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對象
片段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。你或許會看到類似下面的輸出:
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是有值的。
類中的成員變量和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不能被序列化,原因是它們類變量不能序列化。
原文鏈接: javaworld 翻譯: ImportNew.com - xbing譯文鏈接: http://www.importnew.com/12611.html