第11章 序列化

74.谨慎地实现Serializable接口

实现Serializable接口而付出的最大代价是,一旦一个类被发布,就大大降低了“改变这个类的实现”的灵活性。

实现Serializable接口的第二个代价是,它增加了出现bug和安全漏洞的可能性。通常情况下,对象是利用构造器来创建的;序列化机制是一种语言之外的对象创建机制。无论你接受了默认的行为,还是覆盖了默认的方法,反序列化机制都是一个隐藏的构造器,具备与其他构造器相同的特点。

实现Serializable的第三个代价是,随着类发行新的版本,相关的测试负担也增加了。

为了继承而设计的类应该尽可能少地去实现Serializable接口,用户的接口也应该尽可能少地继承Serializable接口。

内部类不应该实现Serializable,他们使用编译器产生的合成域synthetic field来保存指向外围实例的引用,以及保存来自外围作用域的局部变量的值。这些域如何对应到类定义中并没有明确的规定,就好像没有指定匿名类和局部类的名称一样。因此,内部类的默认序列化形式是定义不清楚的。然而,静态成员类static member class可以实现Serializable接口。

75.考虑使用自定义的序列化形式

要为自己编写的每个可序列号的类声明一个显式的序列版本UID。

76.保护性地编写readObject方法

当一个对象被反序列化的时候,对于客户端不应该拥有的对象引用,如果哪个域包含了这样的对象引用,就必须要做保护性拷贝,这是非常重要的。

总而言之,每当你编写readObject方法的时候,都要这样想:你正在编写一个公有的构造器,无论给它传递什么样的字节流,它都必须产生一个有效的实例。

  • 对于对象引用域必须保持为私有的类,要保护性地拷贝这些域中的每个对象。不可变类的可变组件就属于这一类别。
  • 对于任何约束条件,如果检查失败,则抛出一个InvalidObjectException异常。这些检查动作应该跟在所有的保护性拷贝之后。
  • 如果整个对象图在被反序列化之后必须进行验证,就应该使用ObjectInputValidation接口。
  • 无论是直接方式还是间接方式,都不要调用类中任何可被覆盖的方法。

77.对于实例控制,枚举类型优先于readResolve

78.考虑用序列化代理代替序列化实例

results matching ""

    No results matching ""