第74条 谨慎地实现Serializable接口
实现Serializable接口而付出的最大代价是 一旦一个类被发布 就大大降低了 改变这个类的实现 的灵活性
实现Serializable的第二个代价是 它增加了出现Bug和安全漏洞的可能性
实现Serializable的第三个代价是 随着类发行新的版本 相关的测试负担也增加了
实现Serializable接口并不是一个很轻松就可以做出的决定
为了继承而设计的类应该尽可能少地去实现Serializable接口 用户的接口也应该尽可能少地继承Serializable接口
对于为继承而设计的不可序列化的类 你应该考虑提供一个无参构造器
内部类不应该实现Serializable
内部类的默认序列化形式是定义不清楚的
第75条 考虑使用自定义的序列化形式
如果没有先认真考虑默认的序列化形式是否合适 则不要贸然接受
如果一个对象的物理表示法等同于它的逻辑内容 可能就适合于使用默认的序列化形式
即使你确定了默认的序列化形式是合适的 通常还必须提供一个readObject方法以保证约束关系和安全性
当一个对象的物理表示法与它的逻辑数据内容有实质性的区别时 使用默认序列化形式会有以下4个缺点:
它使这个类的导出API永远地束缚在该类的内部表示法上
它会消耗过多的空间
它会消耗过多的时间
它会引起栈溢出
如果所有的实例域都是瞬时的 从技术角度而言 不调用defaultWriteObject和defaultReadObject也是允许的 但是不推荐这样做
在决定将一个域做成非transient的之前 请一定要确信它的值将是该对象逻辑状态的一部分
如果在读取整个对象状态的任何其他方法上强制任何同步 则也必须在对象序列化上强制这种同步
不管你选择了哪种序列化形式 都要为自己编写的每个可序列化的类声明一个显示的序列版本UID
第76条 保护性地编写readObject
当一个对象被反序列化的时候 对于客户端不应该拥有的对象引用 如果哪个域包含了这样的对象引用 就必须要做保护性拷贝 这是非常重要的
不要使用writeUnshared和readUnshared方法
对于对象引用域必须保持为私有的类 要保护性地拷贝这些域中的每个对象 不可变类的可变组件就属于这一类别
对于任何约束条件 如果检查失败 则抛出一个InvalidObjectException异常 这些检查动作应该跟在所有的保护性拷贝之后
如果整个对象图在被反序列化之后必须就行验证 就应该使用ObjectInputValidation接口
无论是直接方法还是间接方式 都不要调用类中任何可被覆盖的方法
第77条 对于实例控制 枚举类型优先于readResolve
如果依赖readResolve进行实例控制 带有对象引用类型的所有实例域则都必须声明为transient的
readResolve的可访问性很重要
第78条 考虑用序列化代理代替序列化实例
每当你发现自己必须在一个不能被客户端扩展的类上编写readObject或者writeObject方法的时候 就应该考虑使用序列化代理模式 要想稳健地将带有重要约束条件的对象序列化时 这种模式可能是最容易得方法