一.对象序列化是什么?
对象总是在运行时存在,当程序终止时,对象也就无论如何也不存在了。而序列化即提供了一种机制,通过将“对象持久化”,可以让对象在程序不运行时也能存在。
二.对象序列化的用途是什么?
- java远程方法调用(
RMI
):可以使得远程对象的像在本机上存在一样。当向远程对象发送消息是,需要通过对象序列化来传出参数和返回值。 - Java Beans:使用bean时需要在设计阶段对它的状态信息进行配置。这种状态信息会保存下来,并在程序启动时进行恢复。
三.保存和加载序列化对象
为了保存对象数据,需要打开一个ObjectOutputStream对象:
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("filename"))
为了保存对象,可以直接用ObjectOutputStream 的writeObject方法
Employee harry = new Employee("Harry Hacker",50000,1989,10,1);
out.writeObject(harry);
为了读回对象,首先要获得一个ObjectInputStream对象:
ObjectInputStream in = new ObjectInputStream(new FileInputStream("filename"))
然后,用readObject方法以这些对象写入顺序获取他们:
Employee e1 = (Employee)in.readObject();
注意:保存和加载基本数据类型时,使用基本数据类型对应的方法,如int类型,使用writeInt和readInd。
四.序列化会持久化对象哪些内容?
1.对象序列化不仅保存了对象的“全景图”,而且能追踪对象对所包含的所有引用,并保存那些对象;接着又能对对象内每个引用进行追踪,找到它的引用类,以此类推,形成一个“对象网”。
2.对象序列化是以特殊格式存储对象数据的,如果有兴趣的可以深入了解“对象序列化的文件格式”。这里只做简单介绍:
存储对象时,对象所属类也必须要存储。这个类的描述包括:
- 类名
- 序列化的版本唯一ID,他是数据域类型和方法签名的指纹。
- 描述序列化方法的标志集。
- 对数据域的描述
值得注意的点:任何类的完整的类描述符只保存一次,后续描述符将引用它(通过序列化唯一ID)。
五.多对象引用的情况
基于“对象网”特性,如果存在存在多个对象对同一个对象进行引用,如两个经理公用同一个秘书:
Employee harry = new Employee("Harry Hacker",50000,1989,10,1);
Manager carl = new Manager (...);
carl.setSecretary(harry);
Manager tony= new Manager (...);
tony.setSecretary(harry);
这种情况是无法通过保存对象的内存地址来保证秘书是同一个人,因为当对象被重载时,他可能重新占据一块新的地址。
序列化保证每个对象都是用同一个序列号保存。下面是它的算法:
- 对遇到的每个对象引用都关联一个序列号
- 对于每个对象,让第一次遇到时,保存其对象数据到输出流中
- 如果某个对象之前被保存过,那么只写出“与之前保存的序列号为x的对象相同”
在读回对象时,则是反向操作: - 输入流的对象,在第一次遇到其序列号时构建它,并使用流中数据进行初始化,然后记录序列号与对象之间的关联
- 当遇到“与之前保存的序列号为x的对象相同”标记时,获取与这个序列号相同的引用
注意:只是同一流中的同一对象会添加“与之前保存的序列号为x的对象相同”标记。不同流时无法保证的。
六.对象序列化的其他方法
上面只介绍了实现Serializable接口的常用序列化方法,序列化包括以下几种方法:
- 含有writeObject方法的类
- 实现了Serializable接口的类
- 实现了Externalizable接口的类
七.屏蔽对象中某些域的方式
有时候我们不希望对象所有的域都被序列化,如用户的密码字段。
这种情况序列化提供了一个transient
(瞬时)关键字,通过对对象中的某些不想要序列化的域添加关键字,即可以保证域不被序列化
还有另外一种处理这种情况的方式--使用Externalizable序列化,后续会进行介绍
八.修改默认的序列化机制
通常我们不需要添加额外的方法,Serializable序列化方式就可以自动实现对对象的序列化。但序列化机制也提供了另外一种方式,来屏蔽自动序列化,向默认的读写行为添加验证或其他行为。
private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException;
private void writeObject(ObjectOutputStream in) throws IOException;
通常,在方法的内部我们需要添加defaultWriteObject
方法和defaultReadObject
方法来保存和加载默认Serializable序列化方式中自动处理的字段(即非transient
字段),而对于transient
字段,则需要通过writeObject
方法和readObject
方法来进行保存和加载(是的,你没有看错,transient
字段只是无法被默认序列化方式保存和加载)。
九.Externalizable序列化方式
Externalizable序列化方式与Serializable序列化方式的不同点在于:
- Externalizable序列化方式不会自动完成序列化,而需要自己定义那些域需要序列化,Externalizable提供了两个方法
writeExternal
和readExternal
,分别显式的保存和加载域。 - Externalizable序列化方式会调用构造器。对于Serializable对象,对象完全以它存储的二进制位为基础来构造,而Externalizable对象则会调用所有默认的构造器(包括字段定义时的初始化,如果无法加载出数据,会初始化为null值)