我经常听到人们说序列化破坏了封装,而这种封装的丢失可以通过提供自定义序列化在一定程度上最小化。有人可以提供一个具体的例子来证明由于默认序列化导致封装丢失是合理的,以及如何通过使用自定义序列化来最小化这种损失?
我将此问题标记为与 Java 相关,但答案可能与语言无关,因为我认为这是跨平台和语言的常见问题。
我经常听到人们说序列化破坏了封装,而这种封装的丢失可以通过提供自定义序列化在一定程度上最小化。有人可以提供一个具体的例子来证明由于默认序列化导致封装丢失是合理的,以及如何通过使用自定义序列化来最小化这种损失?
我将此问题标记为与 Java 相关,但答案可能与语言无关,因为我认为这是跨平台和语言的常见问题。
好问题!首先,让我们定义封装并从那里开始。 这篇维基百科文章以下列方式定义封装:
序列化,至少 Java 这样做的方式,对这两个概念都有影响。当您Serializable
在 Java 中实现接口时,您实际上是在告诉 JVM,您的所有非transient
成员变量及其声明顺序定义了可以从字节流重构对象的协定。当且仅当您的所有成员变量的类定义也都实现Serializable
时,这才递归工作,这就是您可能遇到麻烦的地方。
封装问题
根据前面对封装的定义,尤其是第一项,封装使您无法了解您正在处理的对象实际上是如何工作的,就其成员变量而言。“正确”实现Serializable
会迫使您作为开发人员了解更多关于您正在处理的对象的信息,而不是您可能关心的功能意义上的对象。从这个意义上说,实现Serializable
直接反对封装。
自定义序列化
在每种情况下,序列化都需要了解哪些数据构成特定类型的“对象”。Java 的Serializable
接口通过强制您了解每个您希望序列化transient
的每个成员变量的状态,将这一点发挥到了极致。Object
您可以通过在需要序列化的类型之外定义一个序列化机制来解决这个问题,但是会有设计上的权衡 - 例如,您可能需要在它们实现的接口级别处理对象,而不是与它们的成员变量直接交互,您可能会失去一些从序列化字节流中重建确切对象类型的能力。
Java 默认的序列化逐个字段地写入和读取,这种方式暴露了对象的内部结构,从而破坏了封装。如果您更改类的内部结构,您可能无法正确恢复对象状态。在使用自定义序列化时,如果您更改了类,您可以尝试更改 readObject 以便可以正确恢复保存的对象。