- 深入理解序列化与反序列化
- 潘洪安
- 894字
- 2020-11-21 19:41:47
2.2 Java序列化核心类
2.2.1 Serializable
Serializable类的全路径是java.io.Serializable,JDK源码如下所示。
Serializable是一个空接口,表明了实现自该接口的子类具有序列化行为特征,所有要支持序列化的类都应该实现这个接口。在后面介绍ObjectOutputStream的writeObject方法时,会解释为什么必须这么做。
2.2.2 Externalizable
Externalizable类的全路径是java.io.Externalizable,JDK源码如下所示。
自定义的类必须覆盖writeExternal和readExternal方法。
writeExternal的参数是ObjectOutput,表示输出对象的抽象,它继承自DataOutput,能支持基本类型、String、数组、对象的输出。实际应用中,会使用它的实现类ObjectOutputStream。
readExternal的参数是ObjectInput,表示输入对象的抽象,它继承自DataInput,能支持基本类型、String、数组、对象的输入。实际应用中,会使用它的实现类ObjectInputStream。
自定义的类必须包含无参构造函数。
2.2.3 ObjectOutputStream
java.io.ObjectOutputStream是实现序列化的关键类,它可以将一个对象转换成二进制流,然后通过ObjectInputStream将二进制流还原成对象。为了能更好地理解ObjectOutputStream,先简要说明其内部的关键类。
1)BlockDataOutputStream:作为ObjectOutputStream内置的具有缓冲作用的输出功能类,包含阻塞和非阻塞两种工作模式。两种模式的工作流程相同,都是先把待写的数据写到缓冲区,直到缓冲区满后再执行真正的写入操作。在阻塞模式,每次将缓冲区数据写入之前会写入一个阻塞标记头部(Block Data Header)。
2)HandleTable:当前待序列化的对象,可能包含多个其他对象类型的成员变量,而这些成员变量可能引用同一个对象T,如图2-1所示。
图2-1 一个对象被多个对象引用
为了使得在序列化A对象时,不重复序列化T对象,引入了对象和句柄的映射关系。句柄从0开始按递增顺序自增。每当要写入一个Object类型对象时,去HandleTable查询其是否已经存在,如果存在,则说明已经写入过,本次只写入句柄即可;如果不存在,则说明没有被写入过,需要写入该对象。
3)ReplaceTable:为了支持用户自定义的序列化行为,Java序列化支持对序列化和反序列化进行拦截并执行自定义的序列化行为。ReplaceTable用来管理源对象和目标替换对象,在序列化/反序列化过程中,通过源对象查找目标替换对象,如果存在目标替换对象,则使用目标替换对象;否则使用源对象。
4)ObjectStreamClass:记录了和序列化对象相关的元数据信息,并负责相关方法的反射初始化和执行。ObjectStreamClass包含的核心字段如表2-1所示。
表2-1 ObjectStreamClass的核心字段
续表
2.2.4 ObjectInputStream
java.io.ObjectInputStream是实现Java反序列化的关键类,和ObjectOutputStream是对应的,内部包含了BlockDataInputStream、HandleTable、ReplaceTable、ObjectStreamClass等。这里不展开描述,读者可根据ObjectInputStream的源码自行学习。