内存对象输出外界
序列化接口
java.io.Serializable是一个标识性接口,不包含任何方法
1 | public interface Serializable { |
Serializable提供了一种自动化的解决方案,无需具体实现,非transient
和非static
的成员都是序列化的目标,即便是private
也可以
默认流程
序列化和反序列化本质就对象通过ObjectOutputStream
和ObjectInputStream
进行输入输出
1 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
或者文件保存
1 | FileOutputStream fos = new FileOutputStream("file"); |
自定义序列化
虽然是自动行为,但也提供了一组方法来定制,比较特殊的是这些方法都是private
的,ObjectOutputStream
和ObjectInputStream
会检测用户是否定义了这些钩子方法,进而调用
1 | //定义怎么输出 |
通常自定义不会彻底重写,而是在默认逻辑的基础上加上定制逻辑
out.defaultWriteObject()
和in.defaultReadObject()
提供了默认功能
序列化还有一个子接口Externalizable,可以完全控制过程,脱离自动机制纯手动维护
1 | public interface Externalizable extends java.io.Serializable { |
由于序列化会暴露内部成员的值,因此为了安全性序列化可能会有加解密过程
序列化还提供了回调接口用于在反序列化的最后进行验证
1 | public interface ObjectInputValidation { |
调用顺序
- writeReplace
- writeObject
- readObject
- readResolve
- validateObject
序列化版本控制
实际应用中,序列化类可能发生变化
序列化机制有一定兼容性,反序列化时
- 如果类成员多余,直接忽略
- 如果类成员缺少,使用默认值
序列化类需要定义版本号来表示是否兼容,只有版本号一致的才能进行
1 | private static final long serialVersionUID = 1L; |
这个版本号可以用IDE自动生成,会根据当前类成员情况计算一个哈希值
序列化与类构造
- 无继承的类反序列化不通过构造函数,所以有没有无参构造都无所谓
- 一个类可以序列化,那么它的类成员都必须能够序列化,否则报错
- 父类实现了序列化,子类都具备序列化能力
- 子类实现了序列化,父类没实现,那么父类一定要有无参构造函数。反序列化时首先要调用父类无参构造函数,这时父类的值全是默认值,如果需要同时反序列化父类的值,那么这些工作都需要由子类负责。
- 序列化效果等同于另一种途径的构造,可以作为深度拷贝的手段,有些场景需要保护,比如不可变、单例等等
版权声明
This site by Linest is licensed under a Creative Commons BY-NC-ND 4.0 International License.
由Linest创作并维护的博客采用创作共用保留署名-非商业-禁止演绎4.0国际许可证。
本文永久链接:http://linest.github.io/2018/03/02/java-serialization/