代码之家  ›  专栏  ›  技术社区  ›  Konrad Rudolph

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

  •  22
  • Konrad Rudolph  · 技术社区  · 15 年前

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

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

    我发现一些文本 claim the contrary 但是他们要么写了一个旧版本的JRE(我只对1.6或者1.5感兴趣),要么关注RMI(与我无关)。

    这个 documentation 关于对象标识不是很直接。一 technical article 在sun.com上提到 对象输出流 在对象上使用缓存,这对我来说只有在对象标识确实被保留,但我没有足够的信心来依赖这些脆弱的证据时才有意义。

    我尝试了它(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);
        }
    }
    
    2 回复  |  直到 11 年前
        1
  •  18
  •   ChssPly76    15 年前

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

    1. A和B都是作为同一流的一部分写入并随后从中读取的。这是引自 ObjectInputStream 文档:“使用 引用共享 机制。”
    2. A和B类不重写 readResolve() 这有可能改变引用的恢复方式;持有A和B的类也不可能。

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

        2
  •  10
  •   ocirocir    11 年前

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

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

    但是,对于第一种情况,您可以提供 readResolve 方法以确保返回正确的实例(例如 类型安全枚举 模式)。 读数解析 是JVM在阿德序列化Java对象上调用的私有方法,给对象提供返回不同实例的机会。例如,这就是 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;
        }
    }
    

    .