Update 86. 非常谨慎地实现 Serializable.md

This commit is contained in:
Joe 2019-10-04 10:57:36 +08:00 committed by GitHub
parent 5e6ba00f11
commit a55d08683b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -8,7 +8,7 @@
  可序列化会使类的演变受到限制,施加这种约束的一个简单示例涉及流的唯一标识符,通常称其为串行版本 UID。每个可序列化的类都有一个与之关联的唯一标识符。如果你没有通过声明一个名为 `serialVersionUID` 的静态 final long 字段来指定这个标识符那么系统将在运行时对类应用加密散列函数SHA-1自动生成它。这个值受到类的名称、实现的接口及其大多数成员包括编译器生成的合成成员的影响。如果你更改了其中任何一项例如通过添加一个临时的方法生成的序列版本 UID 就会更改。如果你未能声明序列版本 UID兼容性将被破坏从而在运行时导致 `InvalidClassException`
  **实现 `Serializable` 接口的第二个代价是,增加了出现 bug 和安全漏洞的可能性(第85项)。** 通常,对象是用构造函数创建的;序列化是一种用于创建对象的超语言机制。无论你接受默认行为还是无视它,反序列化都是一个「隐藏构造函数」,其他构造函数具有的所有问题它都有。由于没有与反序列化关联的显式构造函数,因此很容易忘记必须让它能够保证所有的不变量都是由构造函数建立的,并且不允许攻击者访问正在构造的对象内部。依赖于默认的反序列化机制,会让对象轻易地遭受不变性破坏和非法访问(详见第 88 条)。
  **实现 `Serializable` 接口的第二个代价是,增加了出现 bug 和安全漏洞的可能性(详见第 85 条)。** 通常,对象是用构造函数创建的;序列化是一种用于创建对象的超语言机制。无论你接受默认行为还是无视它,反序列化都是一个「隐藏构造函数」,其他构造函数具有的所有问题它都有。由于没有与反序列化关联的显式构造函数,因此很容易忘记必须让它能够保证所有的不变量都是由构造函数建立的,并且不允许攻击者访问正在构造的对象内部。依赖于默认的反序列化机制,会让对象轻易地遭受不变性破坏和非法访问(详见第 88 条)。
  **实现 `Serializable` 接口的第三个代价是,它增加了与发布类的新版本相关的测试负担。** 当一个可序列化的类被修改时,重要的是检查是否可以在新版本中序列化一个实例,并在旧版本中反序列化它,反之亦然。因此,所需的测试量与可序列化类的数量及版本的数量成正比,工作量可能很大。你必须确保「序列化-反序列化」过程成功,并确保它生成原始对象的无差错副本。如果在第一次编写类时精心设计了自定义序列化形式,那么测试的工作量就会减少(详见第 87 和 90 条)。