Saturday, 27 November 2010

Java Serialization Part 2

Great stuff. So we've proved that testing basic Java serialization isn't too hard. So, let's say that I've got a requirement to control instances of this basic object. I'm going to put a ConcurrentMap of instances within the object, make the constructor private and then provide a static factory method on the object to give me the instances.

public class Pojo implements Serializable {
    private static ConcurrentMap instances = new ConcurrentHashMap();

    private String id;

    private Pojo(String id) {
        this.id = id;
    }

    public static Pojo getInstance(String id) {
        Pojo pojo = instances.get(id);
        if (pojo != null) {
            return pojo;
        } else {
            Pojo newPojo = new Pojo(id);
            pojo = instances.putIfAbsent(id, newPojo);
            if (pojo == null) {
                pojo = newPojo;
            }
        }
        return pojo;
    }


   (...snip... equals and hashcode methods provided)
}

So, when I create a unit test looking something like this:

    @Test
    public void shouldBeSingleton() {
        Pojo pojo1 = Pojo.getInstance("id");
        Pojo pojo2 = Pojo.getInstance("id");
        assertTrue(pojo1 == pojo2);
    }


..then the test passes.

Fine. By insisting that clients of this object use the static factory method, then I know that I can control the instances that I create. However, is this true? Turns out that if I test serializing this object to and from a byte array stream, then a simple assertTrue(original == copy) will fail.

The reason? What happens is that on deserializing the stream, a copy of the object is instantiated and then the fields are set, but the deserializing code does not "know" about the getInstance() method on the class.

Fortunately, you can tell it via the readResolve() mechanism. The readResolve() method is applied by the default readObject()  implementation specified in the Java spec.

    private Object readResolve() {
        return Pojo.getInstance(this.id);
    }

One problem of this method is that on deserialization an object is created and then discarded. This may cause garbage collection problems if you're doing this a lot.

Presumably if you provide a readObject() method then you might be able to avoid creating that object at all, so I'll look at that next...

3 comments:

  1. Hi,

    Just to add while writing Serializable class , its good practice to put a Seriazlizable alert in file so that when some one else add a new field in the class he must ensure that field is either transient or Serializable , so that Serializability of class should not break.

    Thanks
    Javin
    Why String is immutable in Java

    ReplyDelete
  2. another point to note about serialization in java is that while de-serialization, the constructor of class is not used. But if the super class is not serializable then a public no-arg constructor should be present in the super class.

    ReplyDelete
  3. Yeah, I completely agree with your opinion and think that serialization not as hard as all people who learn Java think! Maybe they need to learn and read more information about it and do it more insistently. For example there is service that can show you java thread example https://explainjava.com/java-transient/, it was very useful for me when I learned it.

    ReplyDelete