Chapter 18. Serialization

18.1. Performance Consideration - Use Externalization

The best way to achieve performance on Serialization, is to use Externalization without using writeObject.

Example 18.1. Externalization Code

public class FirstClass implements Externalizable
{
        SecondClass secondClass;
        public void writeExternal(ObjectOutputStream out)
        {
                secondClass.writeExternal(out);
        }
        public void readExternal(ObjectInputStream inp)
        {
                secondClass = new SecondClass();
                secondClass.readExternal(inp);
        }
}

class SecondClass implements Externalizable
{
        String str;
        public void writeExternal(ObjectOutputStream out) { out.writeUTF(str); }
        public void readExternal(ObjectInputStream inp) { str = inp.readUTF(); }
}

This is because writeObject will call a heavy discovery meta data for reflection and serialization's constructors. Dealing directly with the life cycle of objects on externalization routines (calling new) will save you from execution this meta code.

Of course there are situations where you have to use writeObject/readObject, specially if the written object was already described as part of other object (for solving circular references), but most of times when doing writeExternal routines you can guarantee the life cycle of objects.

18.2. Version Compatibility

A rule of thumbs is always define serialVersionUID.

Example 18.2. serialVersionUID

private static final long serialVersionUID = 39437495895819393L;

If you don't specify the uniqueID for the object, you can't guarantee version compatibility as minor changes could end up in different serialVersionUID, as they would be calculated according to rules specified on this URL:

http://java.sun.com/j2se/1.5.0/docs/guide/serialization/spec/class.html#4100

18.2.1. In Externalizable Objects

For an externalizable class, writeExternal and readExternal will have to control its version compatibility.

ObjectInputStream encapsulates Streaming in such way that if you try to read more fields from a readExternal method, you would get an EOFException. You could use the exception to determine if the end of a streaming was reached, like in the example

Example 18.3. writeExternal/readExternal among different versions

public void writeExternal(ObjectOutputStream out)
{
   out.writeUTF("FirstString");
   // code added in a newer version
   out.writeUTF("SecondString");
}

public void readExternal(ObjectInputStream inp)
{
   String str1 = inp.readUTF();
   try
   {
      String str2 = inp.readUTF();
   }
   catch (EOFException e)
   {
   }
}

On the example above if an older version was used to write the object an EOFException would happen and it would be ignored. This would guarantee compatibility between different versions.

Any change made to an Externalizable class will be compatible as long as its read and writeExternal methods are compliant.

18.2.2. Regular Serialization

Serialization's specification describe lots of scenarios on exchanging information between different class versions:

http://java.sun.com/j2se/1.5.0/docs/guide/serialization/spec/version.html

Basically there is one simple and basic rules that will summarize the list above

  • Add fields, don't delete them.

You need to take extra care when adding fields if the same Class is used back and forth different versions. For example a Class that is for communications on both sides.

18.2.3. Compatible and Incompatible Changes

The following URL lists all the possible situations where a class will and won't be compatible.

http://java.sun.com/j2se/1.5.0/docs/guide/serialization/spec/version.html