1

我正在阅读ObjectOutputStream 文档并阅读有关序列化的内容,但我有些困惑。它指出

对单个对象的多个引用使用引用共享机制进行编码,以便对象的图形可以恢复到与原始对象相同的形状。

引用共享机制是什么意思?它还指出

枚举常量的序列化方式与普通的可序列化或可外部化对象不同。枚举常量的序列化形式仅由其名称组成;不传输常量的字段值。为了序列化枚举常量,ObjectOutputStream 写入由常量名称方法返回的字符串,就像其他可序列化或可外部化的对象一样,枚举常量可以作为随后出现在序列化流中的反向引用的目标。

我能想到的一个原因是,因为枚举字段是常量,所以它们不会被序列化。那么,如果我们需要序列化一个 Enum,该怎么办呢?另一件事,如果状态不能被序列化,为什么编译器不限制我们序列化一个 Enum 呢?枚举常量可以作为反向引用的目标是什么意思?

请帮助澄清这个疑问。谢谢。

4

3 回答 3

3

使用引用共享机制对单个对象的多个引用进行编码,以便对象的图形可以恢复到与原始对象写入时相同的形状。

这意味着可以序列化包含循环的对象图。

换句话说,如果要序列化下图:

A --> B --> C --> A(C指向A)。

如果您序列化 A 它将执行以下操作:

  1. 序列化一个
  2. 遍历 A 的对象树,找到 B
  3. 序列化 B 的简单字段
  4. 遍历 B 的对象树,找到 C
  5. 序列化 C 的简单字段
  6. 遍历 C 的对象树并找到 A
  7. 将指向 A 的指针发送到流的下游

如果它不这样做,那么序列化 A 将导致无限循环。

枚举常量的序列化方式与普通的可序列化或可外部化对象不同。枚举常量的序列化形式仅由其名称组成;不传输常量的字段值。为了序列化一个枚举常量,ObjectOutputStream 写入常量的 name 方法返回的字符串

因为枚举是常量,所以假设无论在何处使用它们都将始终具有相同的状态,因此没有必要向下发送枚举的状态。因此,当接收方接收并枚举时,它只是在其一侧查找该枚举的对象并将其作为枚举返回,即使任一侧的两个枚举可能具有不同的状态。

于 2013-08-02T09:52:44.710 回答
1

使用引用共享机制对单个对象的多个引用进行编码意味着:

如果您有一个对象图,则该图中的每个对象仅被序列化一次,而不管该图中对它的引用数量如何。例如,如果您在这样的图中有对象 A、B、C 和 D:

在此处输入图像描述

虽然可以通过路径 A -> B -> D 和 A -> C -> D 到达对象 D,但对象序列化机制足够智能,可以在通过第二条路径到达对象 D 时识别出它已经序列化了对象 D,并且所以不要尝试再次序列化它。我怀疑(尽管从来没有仔细研究过)对象图是按引用术语序列化的,并且每个对象都只存储了它的原语和引用。这不仅对最小化序列化对象图的大小很重要,而且对处理循环引用也很重要。

枚举常量的序列化方式与普通的可序列化或可外部化对象不同,因为正如您所说,枚举旨在成为常量。虽然有可能打破这一点,但在一个 JVM 中序列化的枚举在另一个 JVM 中反序列化时看起来会有所不同,这将是向枚举添加运行时状态的结果。

于 2013-08-02T10:02:41.373 回答
0

第一个问题:这意味着显而易见的事情。如果对象出现多次,它将被序列化一次。IE:

Something s1 = new Something(), s2 = new Something();
List<Object> list = new ArrayList<>();
list.add(s1);
list.add(s2);
list.add(s2);
list.add(list);

将序列化两个对象,共享对s2数组列表两个槽中对象的引用。即使你有循环依赖(列表包含对自身的引用),它也会被正确处理 - 对象图按原样传输。

枚举序列化意味着另一个显而易见的事情。它使用枚举名称进行序列化和反序列化。如果您序列化某些东西,通过线路发送它,并使用不同版本的枚举类进行反序列化,则会这样做。例如,当您从中序列化枚举a时:

class MyEnum {a,b,c}

然后在另一个具有如下枚举的 JVM 中反序列化:

class MyEnum {aa, a, bb, c, d}

您将获得相同的a值,尽管枚举的位置(序数)已更改并且可能还有其他一些功能(如哈希码的值)。

于 2013-08-02T09:55:16.560 回答