THE Java™ Programming Language, Fourth Edition

(Jeff_L) #1

Rectangle keeps the serialVersionUID of the original version to declare that the versions are
compatible. Changing fields that would be used by default serialization is otherwise considered to be an
incompatible change.


To represent each of the old fields that will be found in the serialized data, you create an
ObjectStreamField object. You construct each ObjectStreamField object by passing in the name
of the field it represents, and the Class object for the type of the field it represents. An overloaded
constructor also takes a boolean argument that specifies whether the field refers to an unshared objectthat
is, one written by writeUnshared or read by readUnshared. The serialization mechanism needs to
know where to find these ObjectStreamField objects, so they must be defined in the static, final array
called serialPersistentFields.


The fields x, y, width, and height are marked transient because they are not serializedduring
serialization these new fields must be converted into appropriate values of the original fields so that we
preserve the serialized form. So writeObject uses an ObjectOutputStream.PutField object to
write out the old form, using x and y as the old x1 and y1, and calculating x2 and y2 from the rectangle's
width and height. Each put method takes a field name as one argument and a value for that field as the
otherthe type of the value determines which overloaded form of put is invoked (one for each primitive type
and Object). In this way the default serialization of the original class has been emulated and the serialized
format preserved.


When a Rectangle object is deserialized, the reverse process occurs. Our readObject method gets an
ObjectInputStream.GetField that allows access to fields by name from the serialized object. There
is a get method for returning each primitive type, and one for returning an Object reference. Each get
method takes two parameters: the name of the field and a value to return if it is not defined in the serialized
object. The return value's type chooses which overload of get is used: A short return value will use the
get that returns a short, for example. In our example, all values are double: We get the x1 and y1 fields
to use for one corner of the rectangle, and the old x2 and y2 fields to calculate width and height.


Using the above technique the new Rectangle class can deserialize old rectangle objects and a new
serialized rectangle can be deserialized by the original Rectangle class, provided that both virtual machines
are using compatible versions of the serialization stream protocol. The stream protocol defines the actual
layout of serialized objects in the stream regardless of whether they use default serialization or the serialized
field objects. This means that the serialized form of an object is not dependent on, for example, the order in
which you invoke put, nor do you have to know the order in which to invoke getyou can use get or put to
access fields in any order any number of times.


20.8.7. The Externalizable Interface


The Externalizable interface extends Serializable. A class that implements Externalizable
takes complete control over its serialized state, assuming responsibility for all the data of its superclasses, any
versioning issues, and so on. You may need this, for example, when a repository for serialized objects
mandates restrictions on the form of those objects that are incompatible with the provided serialization
mechanism. The Externalizable interface has two methods:


public interface Externalizable extends Serializable {
void writeExternal(ObjectOutput out)
throws IOException;
void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException;
}

Free download pdf