views:

381

answers:

2

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);
    }
}
+8  A: 

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

.

oxbow_lakes
Thanks. Using `readResolve` is exactly what I wanted to avoid since the bookkeeping would become much more complicated than with the typesafe enum pattern in my case. Lucky for me, I’m only interested in single serialization.
Konrad Rudolph
+7  A: 

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.

ChssPly76
put better than me I think
oxbow_lakes
Might want to add the consequences of using the writeUnshared() method of the ObjectOutputStream class, to your answer. It ends up creating new unique objects on the stream. There is more info at the Java Object Serialization Specification at http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf
Vineet Reynolds
Thanks, that’s exactly what I’ve been looking for. In my case, I’m lucky. The `ObjectInputStream` documentation could be more explicit here, though. Without definition, “reference sharing” is a rather opaque term. @Vineet: thanks for linking the specs.
Konrad Rudolph
You're welcome :-)
Vineet Reynolds