Java 序列化和重复对象

2022-09-04 20:54:35

我有以下设置:

public class A {
  private Set<C> cSet;
}

public class B {
  private Set<C> cSet;
}

public class C {}

A 和 B 的 cSet 可能引用相同的 C 实例。我想序列化 A 和 B,以便在反序列化时我没有重复的 C 对象。如果我将Java序列化/反序列化为相同的ObjectOutputStream,Java会知道如何做正确的事情吗,或者我最终会得到比我开始时更多的C实例?


答案 1

不,您不会获得超出必要数量的实例。这是因为在内部,ObjectOutputStream在“句柄表”中注册每个曾经序列化的对象。因此,即使多次调用 writeObject(Object),同一个对象也永远不会被写入两次!C

ObjectOutputStream 的 API 文档:

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

考虑以下代码(假设 和 是可序列化的,并且两者兼而有之,并且有一个指向 以下实例的非瞬态字段):ABCABcC

A a = new A();
B b = new B();
C c = new C();
a.c = c;
b.c = c;

out.writeObject(a); // writes a and c
out.writeObject(b); // writes b and the handle to c

现在让我们读取这两个对象:

A a2 = (A)in.readObject(); // reads a and c
B b2 = (B)in.readObject(); // reads b and handle to c, so b.c points to existing c
// now this is true: a2.c == b2.c

如果您直接将对象写入两次,这也有效,例如:

A a = new A();
out.writeObject(a); // writes a
out.writeObject(a); // writes handle to a

第二个将只写入句柄,而不写入对象本身。这意味着当您再次反序列化对象时,您将不会得到以下两个实例:writeObjectA

A a1 = (A)in.readObject(); // reads a
A a2 = (A)in.readObject(); // reads the handle and returns existing a
// now this is true: a1 == a2

通过调用 reset,您可以清除“句柄表”,以便流的行为类似于新创建的流。

请注意,a 还提供了一个方法 writeObjectUnshared(Object),该方法始终将对象作为新的唯一对象写入流。ObjectOutputStream


答案 2