Java's Serializable interface provides an easy-to-use programming interface for converting between a runtime object and a byte stream. Serialization
involves mapping a runtime object or an object graph into an ObjectOutputStream, which can then be written to the filesystem or stored in a database. Conversely, deserialization reads the byte stream through
the ObjectInputStream and then maps the byte stream into an object or an object graph.
During object serialization, the default Java serialization mechanism writes the metadata about the object, which includes the class name, field names and types, and superclass. This class definition is stored as a part of the serialized object. This stored metadata enables the deserialization process to reconstitute the objects and map the stream data into the class attributes with the appropriate type.
Java serialization offers two scenarios for data interchange. The first scenario is store-and-retrieve object usage—when a Java-based tool or application needs to store the runtime state information in one session and makes it available for a subsequent session. Java serialization allows the state objects to be written into a filesystem or a database, then retrieved later to reconstitute into the proper state objects.
The second scenario involves two runtime environments interoperating through shared objects. In this case, one application, a catalog order, for example, produces a business object, such as an order, which is then handled by another application, say, a fulfillment system, running on a different application server instance. Java serialization enables these business objects to be sent to another server instance by writing the object content as byte stream. The application running on the other server then deserializes the byte stream back into the proper business objects.
Figure 1. Two common scenarios for Java serialization. Click on thumbnail to view full-sized image.
In this article, I use these two scenarios to illustrate best practices for preventing release compatibility from breaking when implementing Java serialization. I begin by discussing how Java serialization provides version control.
In anticipating the need to evolve a serializable class, Java serialization provides a serialVersionUID, also called suid, in the ObjectStreamClass for version control. suid is used to inform the Java serialization mechanism which version of the class is compatible with this serialized object.
However, the importance of this field is often overlooked, resulting in release incompatibility.
package java.io;
public class MySerializableClass implements Serializable {
static String notSerializableString = "static field not serialized";
transient Thread T1; /* Transient field not serialized */
public int aNum = 0;
public String serializedString = "a serialized string";
private void writeObject (ObjectOutputStream s)
throws IOException {
s.defaultWriteObject ();
// Followed by customized serialization code
}
private void readObject (ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject ();
// Followed by customized deserialization code
}
}
The code snippet above shows a basic implementation of the Java Serializable interface. The trace listed below shows the object's use, resulting in an InvalidClassException. This trace reveals a mismatch of the serialVersionUID found in the local class, i.e., the MySerializableClass, and the one stored in the serialized object. In this example, the addition of a non-static field in MySerializableClass results in a different suid associated with this class. This exception illustrates the following:
Archived Discussions (Read only)