Item 86: Implement Serializable with great caution
序列化一个类只需要简单声明让其实现Serializable
接口即可。看起来如此简单以至于被误认为实现序列化对程序员来说很容易。事实却复杂得多,即时代价也许可以忽略,但长期代价却很昂贵。
主要代价之一是:一旦实现了Serializable
序列化接口,发布后的类也就丧失了灵活性。序列化成为暴露的API的一部分。如果采用了序列化的默认实现,当前类的私有字段也成为了API的一部分。采用默认序列化实现后,如果后续修改了类的内部表现(internal representation
),将导致序列化失败。
所以实现序列化接口后,应当仔细设计序列化实现——尽管这将增加一开始时的开发成本,但值得这么做。
实现了序列化接口的类的演化的限制之一是“流统一标识符”(stream unique identifiers
),更通用的名称是“序列号”(serial version UID
)。每个序列化类都有一个独特的标识符。如果没有声明一个静态final类型的long变量serialVersionUID
,系统将在运行时通过SHA-1算法生成这个参数值。类名、实现的接口、大部分成员变量以及编译器生成的合成对象都将影响这个变量的值。如果其中任何一个项目有变化——例如增加一个方法——最终的UID都将发生变化。将抛出InvalidClassException
异常
第二个代价是实现序列化后提高了出现安全漏洞和bug的可能性。通常类初始化通过构造函数,但序列化提高了“语言之外的机制”(extralinguistic mechanism
)完成类初始化动作。
第三个代价是增加了测试成本。新版本发布后,需要验证高低版本之间的序列化转换是否成功。随着版本的增加,序列化的测试成本是指数级的。