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

我使用Java Serializable接口和ObjectOutputStream来序列化对象(到目前为止,这个方法已经足够我的目的)。

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

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

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

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

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

 C----------+ | b1 b2 | +----------+ | | vv 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); } } 

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

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

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

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

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

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