参考:
https://developer.ibm.com/zh/articles/j-lo-serial/
https://blog.csdn.net/moreevan/article/details/6698529
https://www.cnblogs.com/aigeileshei/p/5855823.html
https://www.jianshu.com/p/4935a87976e5
序列化作用:
- 提供一种简单又可扩展的对象保存恢复机制。
- 远程调用时方便对对象进行编码和解码,实现对象直接传输。
- 将对象持久化到介质中,实现对象直接存储。
- 允许对象自定义外部存储的格式。
Java 将对象序列化为二进制文件,在大部分情况下,开发人员只需要使被序列化的类实现 Serializable 接口,使用 ObjectInputStream 和 ObjectOutputStream 进行对象的读写。
需要注意的点:
-
序列化ID(在版本升级时反序列化仍保持对象的唯一性)
private static final long serialVersionUID = 1L;
虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,还取决于两个类的序列化 ID 是否一致。 序列化 ID一般有两种生成策略,一个是固定的 1L,一个是随机生成一个不重复的 long 类型数据。有些时候,通过改变序列化 ID 可以用来限制某些用户的使用。
-
序列化并不保存静态变量
-
要想将父类对象也序列化,需要让父类也实现 Serializable 接口 。若父类不实现,就需要提供显式的无参构造函数 。
一个 Java 对象的构造必须先有父对象,才有子对象。所以反序列化时,为了构造父对象,只能调用父类的无参构造函数作为默认的父对象。因此应该在父类无参构造函数中对变量进行初始化,否则的话,父类变量值都将是默认值。 -
字段不被序列化的方式
1.Transient 关键字修饰
2.将不需要被序列化的字段抽取出来放到父类中,子类实现 Serializable 接口,父类不实现,根据父类序列化规则,父类的字段数据将不被序列化。
3.通过 ObjectStreamField 数组来声明类需要序列化的对象
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("name", String.class),
new ObjectStreamField("age", Integer.class)
};
4.Externalizable是Serializable接口的子类,这个接口的writeExternal()和readExternal()方法可以指定序列化哪些属性;
public void writeExternal(ObjectOutput out) throws IOException {
/*
* 指定序列化时候写入的属性。
*/
out.writeObject(name);
out.writeObject(pwd);
}
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
/*
* 指定反序列化的时候读取属性的顺序以及读取的属性
* 如果你写反了属性读取的顺序,你可以发现反序列化的读取的对象的指定的属性值也会与你写的读取方式一一对应。因为在文件中装载对象是有序的
*/
name=(String) in.readObject();
pwd=(String) in.readObject();
}
- 实现Serializable与Externalizable接口反序列化时区别
实现Serializable 接口的对象序列化文件进行反序列化不走构造方法,载入的是该类对象的一个持久化状态,再将这个状态赋值给该类的另一个对象
实现Externalizable接口的对象序列化文件进行反序列化先走构造方法得到初始对象,然后调用readExternal方法读取序列化文件中的内容给对应的属性赋值。
-
序列化存储规则
当写入文件的为同一对象时,只会存储一份引用。反序列化时,恢复引用关系。(使用ObjectOutputStream类的reset()方法可清除流中保存的写入对象的记录) -
序列化过程中,虚拟机会试图调用对象类里重写的 writeObject 和 readObject 方法,进行用户自定义的序列化和反序列化,若没有,则默认调用 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法。