THE Java™ Programming Language, Fourth Edition

(Jeff_L) #1

20.8.5. Object Versioning


Class implementations change over time. If a class's implementation changes between the time an object is
serialized and the time it is deserialized, the ObjectInputStream can detect this change. When the object
is written, the serial version UID (unique identifier), a 64-bit long value, is written with it. By default, this
identifier is a secure hash of the full class name, superinterfaces, and membersthe facts about the class that, if
they change, signal a possible class incompatibility. Such a hash is essentially a fingerprintit is nearly
impossible for two different classes to have the same UID.


When an object is read from an ObjectInputStream, the serial version UID is also read. An attempt is
then made to load the class. If no class with the same name is found or if the loaded class's UID does not
match the UID in the stream, readObject throws an InvalidClassException. If the versions of all
the classes in the object's type are found and all the UIDs match, the object can be deserialized.


This assumption is very conservative: Any change in the class creates an incompatible version. Many class
changes are less drastic than this. Adding a cache to a class can be made compatible with earlier versions of
the serialized form, as can adding optional behavior or values. Rather then relying on the default serial version
UID, any serializable class should explicitly declare its own serial version UID value. Then when you make a
change to a class that can be compatible with the serialized forms of earlier versions of the class, you can
explicitly declare the serial version UID for the earlier class. A serial version UID is declared as follows:


private static final
long serialVersionUID = -1307795172754062330L;


The serialVersionUID field must be a static, final field of type long. It should also be private since it is
only applied to the declaring class. The value of serialVersionUID is provided by your development
system. In many development systems, it is the output of a command called serialver. Other systems have
different ways to provide you with this value, which is the serial version UID of the class before the first
incompatible modification. (Nothing prevents you from using any number as this UID if you stamp it from the
start, but it is usually a really bad idea. Your numbers will not be as carefully calculated to avoid conflict with
other classes as the secure hash is.)


Now when the ObjectInputStream finds your class and compares the UID with that of the older version
in the file, the UIDs will be the same even though the implementation has changed. If you invoke
defaultReadObject, only those fields that were present in the original version will be set. Other fields
will be left in their default state. If writeObject in the earlier version of the class wrote values on the field
without using defaultWriteObject, you must read those values. If you try to read more values than
were written, you will get an EOFException, which can inform you that you are deserializing an older
form that wrote less information. If possible, you should design classes with a class version number instead of
relying on an exception to signal the version of the original data.


When an object is written to an ObjectOutputStream, the Class object for that object is also written.
Because Class objects are specific to each virtual machine, serializing the actual Class object would not
be helpful. So Class objects on a stream are replaced by ObjectStreamClass objects that contain the
information necessary to find an equivalent class when the object is deserialized. This information includes
the class's full name and its serial version UID. Unless you create one, you will never directly see an
ObjectStreamClass object.


As a class evolves it is possible that a new superclass is introduced for that class. If an older serialized form of
the class is deserialized it will not contain any serialized data for that superclass. Rather than making this an
error, the system will set all fields declared by the superclass to their default initialized values. To override
this default behavior, the new superclass (which must implement Serializable, of course) can declare the
following method:

Free download pdf