11 序列化(85-90)

Wu Jun 2020-01-02 15:51:27
05 Java > 02 Effective Java

第 85 条: 首选 Java 序列化的替代品

序列化是危险的,反序列化有漏洞,应该避免。

使用跨平台的结构化数据替代,例如 JSON 或 protobuf。

或者不反序列化不受信任的数据。

如果必须反序列化,使用对象反序列化过滤,并避免编写可序列化的类。

第 86 条:谨慎地实现 Serializable 接口

Serializable 的成本
考虑点

第 87 条:考虑使用自定义的序列化形式

一般来讲,只有当你自行设计的自定义序列化形式与默认的形式基本相同时,才能接受默认的序列化形式。

用户可以通过 writeObject() 和 readObject() 自定义序列化和反序列化逻辑。对一些敏感信息加密的逻辑也可以放在此。

无论你是否使用默认的序列化形式,如果在读取整个对象状态的任何其他方法上强制任何同步,则也必须在对象序列化上强制这种同步。

第 88 条:保护性地编写 readObject 方法

对于非 final 的可序列化类,在 readObject 方法和构造器之间还有其他类似的地方,readObject 方法不可以调用可被覆盖的方法,无论是直接调用还是间接调都不可以。如果违反了该规则,并且覆盖了该方法,被覆盖的方法将在子类的状态被反序列化之前先运行。程序很可能会失败。

第 89 条:对于实例控制枚举类型优先于 readResolve

如果这个类的声明中加上了“implements Serializable”的字样,它就不再是一个 Singleton。

如果依赖 readResolve 进行实例控制,带有对象引用类型的所有实例域则都必须声明为 transient 的。

将一个可序列化的实例受控的类编写成枚举,就可以绝对保证除了所声明的常量之外,不会有别的实例。

第 90 条:考虑用序列化代理代替序列化实例

首先,为可序列化的类设计一个私有的静态嵌套类,精确地表示外围类的实例的逻辑状态。这个嵌套类被称作序列化代理(serialization proxy),它应该有一个单独的构造器,其参数类型就是那个外围类。这个构造器只从它的参数中复制数据:它不需要进行任何一致性检查或者保护性拷贝。按设计,序列代理的默认序列化形式是外围类最好的序列化形式。外围类及其序列代理都必须声明实现 Serializable 接口。

每当你发现自己必须在一个不能被客户端扩展的类上编写 readObject 或者 riteObject 方法的时候,就应该考虑使用序列化代理模式。要想稳健地将带有重要约束条件的对象序列化时,这种模式可能是最容易的方法。