【转】在Java中如何使用transient
原文链接 http://www.importnew.com/12611.html
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()); } } } class Foo implements Serializable |
片段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。
但是这个输出并没有告诉我们,是否变量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
[ 转载请保留原文出处、译者和译文链接。]
关于作者: xbing
:::
英文原文写的非常好,只是翻译。。。。。