Java語言的transient不像class、synchronized和其餘熟悉的關鍵字那樣衆所周知,於是它會出如今一些面試題中。這篇文章我將爲你們講解transient。java
Q:transient關鍵字能實現什麼?node
A:當對象被序列化時(寫入字節序列到目標文件)時,transient阻止實例中那些用此關鍵字聲明的變量持久化;當對象被反序列化時(從源文件讀取字節序列進行重構),這樣的實例變量值不會被持久化和恢復。例如,當反序列化對象——數據流(例如,文件)可能不存在時,緣由是你的對象中存在類型爲java.io.InputStream的變量,序列化時這些變量引用的輸入流沒法被打開。面試
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
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不能被序列化,緣由是它們類變量不能序列化。