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

2022-09-01 19:12:23

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

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

我发现一些文本声称相反 - 但他们要么写了一个旧版本的JRE(我只对1.6感兴趣,也许对1.5感兴趣),要么关心RMI(这与我无关)。

关于对象标识的文档不是很简短。一篇关于 sun.com 的技术文章提到了对对象使用缓存,对我来说,这只有在确实保留了对象标识时才有意义,但我没有足够的信心来依赖这些脆弱的证据。ObjectOutputStream

我已经尝试过(Java 1.6,OS X),发现是的通过序列化,对象的标识保持不变。但是,我可以从这些结果中推断出来,还是不可靠?

对于我的测试,我序列化了以下对象图:

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);
    }
}

答案 1

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

  1. a 和 b 都写为同一流的一部分,随后又从中读取。以下是来自 ObjectInputStream 文档的一段话:“使用引用共享机制正确还原对象的图形。
  2. a类和b类不会覆盖有可能改变引用恢复方式的重写;持有 a 和 b 的类也没有。readResolve()

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


答案 2

答案是否定的,默认情况下,如果您正在考虑对给定对象/图形进行 2 个单独的序列化,则不会通过序列化保留对象标识。例如,如果 I 通过网络序列化一个对象(也许我通过 RMI 将其从客户端发送到服务器),然后再次执行此操作(在单独的 RMI 调用中),则服务器上的 2 个反序列化对象将不会是 ==。

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

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

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;
    }
}

.