在Java中,我们可以用ObjectInputStream类和ObjectOutputStream类来实现对象的输入和输出,但是并非每一个对象都可以写到输入流中,我们把可以写入输入流中的对象称之为可序列化的,也就是Serializable的。在Java中,为了标识一个对象可以写入输出流,该对象的类必需实现Serializable接口,每个可序列化的对象都是该接口的一个实例。
如上所说,Serializable接口只是个标识,其内部并没有方法:
public interface Serializable { }
实现该接口即可启动Java的序列化机制,自动完成存储对象和数组的过程。Java提供一个内在机制自动完成写对象的过程,这个过程称为对象序列化,它是在ObjectOutputStream中实现的;反过来呢,读取对象的过程称作对象反序列化,它是在ObjectInputStream中实现的。
考虑如下工程:
项目中我们有三个类,其中ClassA是个空类,仅仅实现了Serializable接口:
package com.aigestudio.test; import java.io.Serializable; public class ClassA implements Serializable { }
而SerializableClass类中我们定义了如下几个字段:
package com.aigestudio.test; import java.io.Serializable; public class SerializableClass implements Serializable { private String strA; private int intA; private boolean booA; private ClassA classA = new ClassA(); }
其中,我们声明并实例化了ClassA类的一个实例对象,在Test中我们将SerializableClass的一个实例对象写入文本:
package com.aigestudio.test; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; public class Test { public static void main(String[] args) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("H:\\test.dat")); oos.writeObject(new SerializableClass()); oos.close(); } }
运行后我们可以看到在文本中已经写入了二进制数据:
当试图往输出流中写入SerializableClass的对象时,Java会先检测SerializableClass对象中的所有对象是否都是可序列化的(我们代码中的classA对象),如果发现有不能序列化的对象,则会报出NotSerializableException异常。比如我将上面ClassA改为如下:
package com.aigestudio.test; public class ClassA { }
去掉了Serializable接口的实现,运行程序即会报出异常:
因为我们在Test中试图输出SerializableClass对象时发现其中有不可序列化的ClassA对象。
如果我们想输出SerializableClass对象而不需要管ClassA对象且不想让ClassA对象实现Serializable接口,这时该怎么办呢?很简单,我们只需在声明时给ClassA对象加上transient关键字即可,这时Java在对SerializableClass对象进行编码时就会忽略掉ClassA对象:
package com.aigestudio.test; import java.io.Serializable; public class SerializableClass implements Serializable { private String strA; private int intA; private boolean booA; private <span style="color:#990000;"><strong>transient</strong></span> ClassA classA = new ClassA(); }
编译运行:
= =虽然咱们看不懂,但是数据已变对吧、、、呵呵呵呵呵呵呵……
除了transient关键字外,静态变量也会被忽略写入输出流,具体就不演示了~有兴趣自己可以去试试……
当存储一个可序列化对象时,会对该对象的类进行编码。编码包括类名、类的签名、对象实例变量的值以及从初始对象引用的任何其他对象的闭包,但是不存储对象静态变量的值。
当第一次写入一个对象时,会为该对象创建一个序列号,Java虚拟机将对象的所有内容和序列号一起写入对象流,以后每次存储如果再写入相同的对象就只存储序列号。读取这些对象时,它们的引用都相同,因为在内存中实际上存储的只是一个对象。