transient 的用途
Q:transient 关键字能实现什么?
A:当对象被序列化时(写入字节序列到目标文件)时,transient 阻止实例中使用此关键字声明的变量持久化;当对象被反序列化时(从源文件读取字节序列进行重构),这样的实例变量值不会被持久化和恢复。例如,当反序列化对象——数据流(例如,文件件)可能不存在时,原因是你的对象中存在类型为java.io.InputStream的变量,反序列化时这些变量引用的输入流无法被打开。
transient 使用介绍
Q:如何使用transient?
A:包含实例变量声明中的transient修饰符。片段1提供了小的演示
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; //自动封装 Integer
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。你或许会看到类似下面的输出:
ClassLib(InputStream) called
Minor version number: 0
Major version number: 51
java.io.FileInputStream@79f1e0e0
Minor version number: 0
Major version number: 51
null
类中的成员变量和static
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 Foo implements Serializable
{
public static int w = 1;
public static transient int x = 2;
public int y = 3;
public transient int z = 4;
}
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());
}
}
}
运行结果:
w: 1
x: 2
y: 3
z: 4w: 1
x: 2
y: 3
z: 0
这个输出告诉我们,实例变量y是被序列化的,z却没有,因为它被标记transient。但是,当Foo被序列化时,它并没有告诉我们,是否变量w和x被序列化和反序列化,是否只是以普通类初始化方式初始。对于答案,我们需要查看x.ser的内容。
下面显示x.ser十六进制:
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不能被序列化,原因是它们类变量不能序列化。
transient和static的序列化和反序列化
可以参考链接:# 关于transient和static的序列化和反序列化
以下是个人总结:
- 被static修饰的变量不会被序列化的,因为只有堆内存会被序列化.所以静态变量会天生不会被序列化。
- 被static修饰的变量反序列化后有值原因: 静态变量在方法区,本来流里面就没有写入静态变量,我们打印静态变量会去方法区查找
感谢
原文链接: javaworld 翻译: ImportNew.com - xbing
译文链接: http://www.importnew.com/12611.html
参考地址:关于transient和static的序列化和反序列化