第5章 序列化
19 谨慎的实现Serializable接口(Implement Serializable judiciously)
实现Serializable接口的代价有:当类被发布之后,改变该类的内部实现的灵活性降低了;增加了出错的可能和安全漏洞;当发布类的新版本时,测试的负担增大了,因为需要保证新类与旧类是互相可序列化的。
实现Serializable接口不是一个简单的决策。
被设计用来继承的类很少实现Serializable接口,其它接口也很少继承它。
对于设计用来被继承的不可序列化的类,应该考虑提供一个无参数的构造函数。
内部类应该很少实现Serializable接口。而静态成员类可以实现Serializable接口。
20 考虑使用自定义的序列化形式(Consider using a custom serialized form)
不要不经考虑就使用默认的序列化形式。
当一个对象的物理表示和逻辑内容一致时,可以使用默认的序列化形式。
就算你认为默认的序列化形式是合适的,你也必须提供一个readObject方法来保证不变性和安全性。
如果所有的域都是transient的,可以不需要调用defaultWriteObject方法,但是不推荐这么做。
21 防御性的编写readObject方法(Write readObject methods defensively)
当对象被发序列化的时候,很重要的一点是防御性的复制任何客户端不应该拥有的包含对象引用的域。
一个readObject方法不应该直接或间接的调用任何可以被重载的方法。
以下是一些正确编写readObject方法的建议:
1) 对于必须保持私有的对象引用域,防御性的复制该域所保存的对象。
2) 对于有不变性的类,检查不变性,如果不满足的话,就抛出InvalidObjectException。检查跟在防御性复制之后。
3) 如果在反序列化之后整个对象图需要进行验证,可以使用ObjectInputValidation接口。
4) 不在方法中直接或间接的调用任何可被重载的方法。
22 必要是提供readResolve方法(Provide a readResolve method when necessary)
任何readObject方法,不论是显示的还是默认的,都返回的是一个新创建的实例,而不是类初始化的时候创建的那个实例。
一个readResolve方法不仅对于单件是必要的,对于任何其它instance-controlled的类也是。
一条经验规则是如果你编写一个没有公共和保护构造函数的可序列化类,考虑它是否需要一个readResolve方法。
该readResolve方法的另外一个作用是可以作为防御性readObject的替代。
防御性的readResolve不适合允许包外继承的类。
该readResolve方法的可访问性很重要。
在允许继承的类中,readResolve方法不能替代readObject。