【问题标题】:How encapsulation is broken while accepting default Serialization?接受默认序列化时如何破坏封装?
【发布时间】:2023-10-23 16:36:02
【问题描述】:

我经常听到人们说序列化破坏了封装,而这种封装的丢失可以通过提供自定义序列化在一定程度上最小化。有人可以提供一个具体的例子来证明由于默认序列化而导致的封装损失是合理的,以及如何通过使用自定义序列化来最小化这种损失?

我将此问题标记为与 Java 相关,但答案可能与语言无关,因为我认为这是跨平台和语言的常见问题。

【问题讨论】:

  • 这个article 提供了一个很好的例子来说明序列化如何破坏封装

标签: java serialization deserialization encapsulation information-hiding


【解决方案1】:

很好的问题!首先,让我们定义封装并从那里开始。 This wikipedia article 以如下方式定义封装:

  • 一种用于限制对某些对象组件的访问的语言机制。
  • 一种语言结构,有助于将数据与操作该数据的方法(或其他函数)捆绑在一起。

序列化,至少 Java 这样做的方式,对这两个概念都有影响。当您在 Java 中实现 Serializable 接口时,您实际上是在告诉 JVM 您的所有非transient 成员变量以及声明它们的顺序定义了可以从字节流中重构对象的协定。当且仅当您的所有成员变量的类定义也实现Serializable 时,这才递归有效,这就是您可能遇到麻烦的地方。

封装问题

根据前面对封装的定义,尤其是第一项,封装使您无法了解您正在处理的对象实际上是如何工作的,就其成员变量而言。 “正确”实现Serializable 会迫使您作为开发人员更多地了解您正在处理的对象,而不是您可能关心的功能意义上的对象。从这个意义上说,实现Serializable直接反对封装。

自定义序列化

在任何情况下,序列化都需要了解哪些数据构成特定类型的“对象”。 Java 的Serializable 接口通过强制您了解您希望序列化的每个Object 的每个成员变量的transient 状态将这一点发挥到了极致。您可以通过在需要序列化的类型之外定义一个序列化机制来解决这个问题,但是会有设计权衡 - 例如您可能需要在对象实现的接口级别处理对象,而不是与其成员变量直接交互,并且您可能会失去一些从序列化字节流中重建确切对象类型的能力。

【讨论】:

    【解决方案2】:

    Java 默认序列化逐个字段地写入和读取,这种方式暴露了对象的内部结构,从而破坏了封装。如果您更改类的内部结构,您可能无法正确恢复对象状态。使用自定义序列化时,如果您更改了类,您可以尝试更改 readObject 以便正确恢复保存的对象。

    【讨论】: