25

我正在使用 JavaSerializable接口和ObjectOutputStream序列化对象(到目前为止,这种方法已经足够我的目的了)。

我的 API 依赖于对象身份进行某些操作,我想知道它是否会被序列化保留。也就是说:如果对于任意两个对象aba == 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);
    }
}
4

2 回答 2

20

对于两个任意对象a和b,如果在序列化之前它持有a == b,那么在反序列化之后它仍然会成立IF:

  1. a 和 b 都作为同一流的一部分写入并随后从中读取。这是来自ObjectInputStream文档的引用:“对象的图形使用引用共享机制正确恢复。”
  2. a 和 b 的类不会覆盖readResolve()有可能改变引用恢复方式的可能性;持有 a 和 b 的课程也没有。

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

于 2009-08-29T15:57:33.110 回答
11

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

然而,在“单序列化”中,例如单个客户端-服务器消息,它是一个多次包含相同对象的图,然后在反序列化时,身份保留。

但是,对于第一种情况,您可以提供readResolve方法的实现以确保返回正确的实例(例如,在类型安全的枚举模式中)。readResolve是一个私有方法,将由 JVM 在反序列化的 Java 对象上调用,使对象有机会返回不同的实例。例如,这就是在's 添加到语言TimeUnit enum之前可能已经实现的方式: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;
    }
}

.

于 2009-08-29T15:46:23.150 回答