序列化是否保留对象身份?

发布于 2024-08-03 11:53:06 字数 2228 浏览 4 评论 0原文

我使用 Java Serialized 接口和 ObjectOutputStream 来序列化对象(到目前为止,此方法已经足以满足我的目的)。

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

I am using the Java Serializable interface and the ObjectOutputStream to serialize objects (until now, this method has been sufficient for my purposes).

My API relies on object identity for some operations and I’m wondering if it will be preserved by serialization. That is to say: if, for two arbitrary objects a and b, it holds a == b before serialization, does it still hold after deserialization?

I’ve found some texts that claim the contrary – but they either wrote about an older version of the JRE (I’m only interested in 1.6 and perhaps 1.5), or were concerned with RMI (which is not relevant for me).

The documentation isn’t very forthcoming regarding object identity. A technical article on sun.com mentions that ObjectOutputStream uses caching on objects, which to me only makes sense if the object identity is indeed preserved but I’m not confident enough to rely on this flimsy evidence.

I’ve tried it out (Java 1.6, OS X) and found that yes, the identity of objects remains unchanged by serialization. But can I extrapolate from these results or are they unreliable?

For my test, I’ve serialized the following object graph:

C----------+
| b1    b2 |
+----------+
  |      |
  v      v
B---+  B---+
| a |  | a |
+---+  +---+
   \    /
    \  /
     \/
   A----+
   |    |
   +----+

A minimal reproducing code:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

紫轩蝶泪 2024-08-10 11:53:06

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

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

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

For two arbitrary objects a and b, if it holds a == b before serialization, it will still hold true after deserialization IF:

  1. Both a and b are written as and subsequently read from as parts of the same stream. Here's a quote from ObjectInputStream documentation: "Graphs of objects are restored correctly using a reference sharing mechanism."
  2. Class of a and b does not override readResolve() that has the potential of changing how references are restored back; neither do classes that hold a and b.

For all other cases, object identity will NOT be preserved.

∞琼窗梦回ˉ 2024-08-10 11:53:06

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

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

但是,对于第一种情况,您可以提供 readResolve 方法,以确保返回正确的实例(例如,在类型安全枚举模式中)。 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;
    }
}

The answer is no, by default object identity is not preserved via serialization if you are considering 2 separate serializations of a given object/graph. For example, if a I serialize an object over the wire (perhaps I send it from a client to a server via RMI), and then do it again (in separate RMI calls), then the 2 deserialized objects on the server will not be ==.

However, in a "single serialization" e.g. a single client-server message which is a graph containing the same object multiple times then upon deserialization, identity is preserved.

For the first case, you can, however, provide an implementation of the readResolve method in order to ensure that the correct instance is returned (e.g. in the typesafe enum pattern). readResolve is a private method which will be called by the JVM on a de-serialized Java object, giving the object the chance to return a different instance. For example, this is how the TimeUnit enum may have been implemented before enum's were added to the language:

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

.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文