Java 序列化是否保留对象标识?

Java 序列化是否保留对象标识?,java,serialization,identity,Java,Serialization,Identity,我正在使用JavaSerializable接口和ObjectOutputStream来序列化对象(到目前为止,这种方法对于我来说已经足够了) 我的API在某些操作中依赖于对象标识,我想知道是否可以通过序列化来保留它。也就是说:如果对于两个任意对象a和b,它在序列化之前保持a==b,那么在反序列化之后它仍然保持吗? 我发现了一些文本,但它们要么是关于JRE的旧版本(我只对1.6和1.5感兴趣),要么是关于RMI(这与我无关) 关于对象标识,这个问题还不是很明确。sun.com上的一篇文章提到,Ob

我正在使用Java
Serializable
接口和
ObjectOutputStream
来序列化对象(到目前为止,这种方法对于我来说已经足够了)

我的API在某些操作中依赖于对象标识,我想知道是否可以通过序列化来保留它。也就是说:如果对于两个任意对象
a
b
,它在序列化之前保持
a==b
,那么在反序列化之后它仍然保持吗?

我发现了一些文本,但它们要么是关于JRE的旧版本(我只对1.6和1.5感兴趣),要么是关于RMI(这与我无关)

关于对象标识,这个问题还不是很明确。sun.com上的一篇文章提到,
ObjectOutputStream
在对象上使用缓存,这对我来说只有在对象标识确实被保留的情况下才有意义,但我没有足够的信心去依赖这一站不住脚的证据

我已经尝试过了(Java1.6,OSX),发现是的,通过序列化,对象的标识保持不变。但我能从这些结果中推断出来吗?还是它们不可靠

在测试中,我序列化了以下对象图:

C----------+
| b1    b2 |
+----------+
  |      |
  v      v
B---+  B---+
| a |  | a |
+---+  +---+
   \    /
    \  /
     \/
   A----+
   |    |
   +----+
最小复制代码:

import java.io.*;

public class SerializeTest {
    static class A implements Serializable {}

    static class B implements Serializable {
        final A a;

        public B(A a) {
            this.a = a;
        }
    }

    static class C implements Serializable {
        final B b1, b2;

        public C() {
            A object = new A();
            b1 = b2 = new B(object);
        }
    }

    public static void main(String[] args) throws IOException,
            ClassNotFoundException {
        C before = new C();
        System.out.print("Before: ");
        System.out.println(before.b1.a == before.b2.a);

        // Serialization.
        ByteArrayOutputStream data = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(data);
        out.writeObject(before);
        out.close();

        // Deserialization.
        ObjectInputStream in =
            new ObjectInputStream(new ByteArrayInputStream(data.toByteArray()));
        C after = (C) in.readObject();
        System.out.print("After: ");
        System.out.println(after.b1.a == after.b2.a);
    }
}

答案是否,默认情况下,如果考虑对给定对象/图形进行两次单独的序列化,则不会通过序列化保留对象标识。例如,如果我通过线路序列化一个对象(可能是通过RMI从客户机发送到服务器),然后再次执行(在单独的RMI调用中),那么服务器上的2个反序列化对象将不会为==

然而,在“单一序列化”中,例如,单一客户机-服务器消息是一个多次包含同一对象的图形,然后在反序列化时,标识被保留

但是,对于第一种情况,您可以提供该方法的实现,以确保返回正确的实例(例如,在typesafe枚举模式中)
readResolve
是一个私有方法,JVM将在反序列化Java对象上调用该方法,使该对象有机会返回不同的实例。例如,在将
enum
添加到语言之前,
TimeUnit
enum
可能就是这样实现的:

public class TimeUnit extends Serializable {

    private int id;
    public TimeUnit(int i) { id = i; }
    public static TimeUnit SECONDS = new TimeUnit(0);

    //Implement method and return the relevant static Instance
    private Object readResolve() throws ObjectStreamException {
        if (id == 0) return SECONDS;
        else return this;
    }
}

.

对于两个任意对象a和b,如果它在序列化之前保持a==b,则在反序列化之后仍然保持true,如果:

  • a和b都作为同一流的一部分写入,然后作为同一流的一部分从中读取。这里引用了文档中的一句话:“使用引用共享机制正确恢复对象的图形。”
  • 类a和类b不重写
    readResolve()
    ,该类可能会更改恢复引用的方式;持有a和b的课程也不例外

  • 对于所有其他情况,对象标识将不会被保留。

    可能需要将使用ObjectOutputStream类的writeUnshared()方法的结果添加到您的答案中。它最终会在流上创建新的唯一对象。在Java对象序列化规范中有更多信息,谢谢,这正是我一直在寻找的。就我而言,我很幸运。然而,
    ObjectInputStream
    文档在这里可能更明确。没有定义,“参考文献共享”是一个相当不透明的术语@维内特:谢谢你链接规格。谢谢。使用
    readResolve
    正是我想要避免的,因为在我的例子中,簿记将比使用typesafe枚举模式复杂得多。幸运的是,我只对单个序列化感兴趣。如果它认为a==b,那么它们不是任意对象。我相信你的问题适用于参考资料。