https://www.cnblogs.com/Kidezyq/p/8025176.html
https://www.iteye.com/blog/moon-walker-2376857
https://www.iteye.com/blog/moon-walker-2376922
https://www.iteye.com/blog/moon-walker-2377021
https://www.iteye.com/blog/moon-walker-2377422
https://blog.csdn.net/lwj_zeal/article/details/90743500
Serializable 用来标识当前类可以被 ObjectOutputStream 序列化,以及被 ObjectInputStream 反序列化。
Serializable 接口的基本使用
通过 ObjectOutputStream 将需要序列化数据写入到流中,因为 Java IO 是一种装饰者模式,因此可以通过 ObjectOutStream 包装 FileOutStream 将数据写入到文件中或者包装 ByteArrayOutStream 将数据写入到内存中。同理,可以通过 ObjectInputStream 将数据从磁盘 FileInputStream 或者内存 ByteArrayInputStream 读取出来然后转化为指定的对象即可。
Serializable接口的特点:
1. 序列化类的属性没有实现 Serializable 那么在序列化就会报错
2. 在反序列化过程中,它的父类如果没有实现序列化接口,那么将需要提供无参构造函数来重新创建对象。
如果要序列化的对象的父类 没有实现序列化接口,那么在反序列化时是会调用对应的无参构造方法的,这样做的目的是重新初始化父类的属性,例如 父类 因为没有实现序列化接口,因此对应的属性就不会被序列化,因此反序列得到的 属性值就为 null。
3. 一个实现 Serializable 接口的子类也是可以被序列化的。
序列化是针对对象属性的,而静态成员变量是属于类的。
6.Serializable 在序列化和反序列化过程中大量使用了反射,因此其过程会产生的大量的内存碎片
serialVersionUID
用于确保类序列化与反序列化的兼容性问题的,如果序列化和反序列化过程中这两个值不一样,那么将导致序列化失败
序列化使用一个 hash,该 hash 是根据给定源文件中几乎所有东西 — 方法名称、字段名称、字段类型、访问修改方法等 — 计算出来的,序列化将该 hash 值与序列化流中的 hash 值相比较。
为了使 Java 运行时相信两种类型实际上是一样的,第二版和随后版本必须与第一版有相同的序列化版本 hash(存储为 private static final serialVersionUID 字段)。
serialVersionUID 发生改变有三种情况:
手动去修改导致当前的 serialVersionUID 与序列化前的不一样。
我们根本就没有手动去写这个 serialVersionUID 常量,那么 JVM 内部会根据类结构去计算得到这个 serialVersionUID 值,在类结构发生改变时(属性增加,删除或者类型修改了)这种也是会导致 serialVersionUID 发生变化。
假如类结构没有发生改变,并且没有定义 serialVersionUID ,但是反序列和序列化操作的虚拟机不一样也可能导致计算出来的 serialVersionUID 不一样。
JVM 规范强烈建议我们手动声明一个版本号,这个数字可以是随机的,只要固定不变就可以。同时最好是 private 和 final 的,尽量保证不变。
Externalizable 接口
Serializable 接口内部序列化是 JVM 自动实现的,如果想自定义序列化过程,就可以使用Externalizable接口来实现
Java 的序列化步骤与数据结构分析
序列化算法一般会按步骤做如下事情:
- 将对象实例相关的类元数据输出。
- 递归地输出类的超类描述直到不再有超类。
- 类元数据完了以后,开始从最顶层的超类开始输出对象实例的实际数据值。
- 从上至下递归输出实例的数据
只有实现了 Serializable 或 Externalizable 接口的类的对象才能被序列化,否则抛出异常。
对于实现了这两个接口,具体序列化和反序列化的过程又分以下3中情况:
情况1:若类仅仅实现了Serializable接口,则可以按照以下方式进行序列化和反序列化
ObjectOutputStream采用默认的序列化方式,对对象的非transient的实例变量进行序列化。
ObjcetInputStream采用默认的反序列化方式,对对象的非transient的实例变量进行反序列化。
情况2:若类不仅实现了Serializable接口,并且还定义了readObject(ObjectInputStream in)和writeObject(ObjectOutputSteam out),则通过反射方式进行序列化与反序列化。
ObjectOutputStream调用对象的writeObject(ObjectOutputStream out)的方法进行序列化。
ObjectInputStream会调用对象的readObject(ObjectInputStream in)的方法进行反序列化。
情况3:若类实现了Externalnalizable接口,且类必须实现readExternal(ObjectInput in)和writeExternal(ObjectOutput out)方法,则按照以下方式进行序列化与反序列化。
ObjectOutputStream调用对象的writeExternal(ObjectOutput out))的方法进行序列化。
ObjectInputStream会调用对象的readExternal(ObjectInput in)的方法进行反序列化。
java对象的序列化分成两部分,前一部分是对类以及成员的描述进行序列化(元数据),第二部分是对成员的值进行序列化。Java序列化框架的核心类包括:ObjectOutputStream、ObjectInputStream、ObjectStreamClass、ObjectStreamField、ObjectStreamConstants。其中序列化和反序列化的主要业务逻辑都在ObjectOutputStream、ObjectInputStream中完成,ObjectStreamClass里主要存放类描述信息,ObjectStreamField存放成员变量描述信息,ObjectStreamConstants存放的主要是一些16进制的常量。