THE Java™ Programming Language, Fourth Edition

(Jeff_L) #1

state, you will almost certainly need to customize the first serializable class (see the next section). If the first
serializable class directly extends Object (as the earlier Name class did), customizing is easy because
Object has no state to preserve or restore.


Once the first serializable class has finished with its part of its superclass's state, it will set its own state from
the stream. Then ObjectInputStream will walk down the type tree, deserializing the state for each class
using readObject. When ObjectInputStream reaches the bottom of the type tree, the object has been
completely deserialized.


As the stream is deserialized, other serialized objects will be found that were referenced from the object
currently being deserialized. These other objects are deserialized as they are encountered. Thus, if URLInput
had a reference to a HashMap, that hash map and its contents would be deserialized before the HTTPInput
part of the object was deserialized.


Before any of this can happen, the relevant classes must first be loaded. This requires finding a class of the
same name as the one written and checking to see that it is the same class. You'll learn about versioning issues
shortly. Assuming it is the same class, the class must be loaded. If the class is not found or cannot be loaded
for any reason, readObject will throw a ClassNotFoundException.


20.8.4. Customized Serialization


The default serialization methods work for many classes but not for all of them. For some classes default
deserialization may be improper or inefficient. The HashMap class is an example of both problems. Default
serialization would write all the data structures for the hash map, including the hash codes of the entries. This
serialization is both wrong and inefficient.


It is wrong because hash codes may be different for deserialized entries. This will be true, for example, of
entries using the default hashCode implementation.


It is inefficient because a hash map typically has a significant number of empty buckets. There is no point in
serializing empty buckets. It would be more efficient to serialize the referenced keys and entries and rebuild a
hash map from them than to serialize the entire data structure of the map.


For these reasons, java.util.HashMap provides private writeObject and readObject methods.[3]
These methods are invoked by ObjectOutputStream and ObjectInputStream, respectively, when it
is time to serialize or deserialize a HashMap object. These methods are invoked only on classes that provide
them, and the methods are responsible only for the class's own state, including any state from non-serializable
superclasses. A class's writeObject and readObject methods, if provided, should not invoke the
superclass's readObject or writeObject method. Object serialization differs in this way from clone
and finalize.


[3] These methods are private because they should never be overridden and they should
never be invoked by anyone using or subclassing your class. The serialization mechanism
gains access to these private methods using reflection to disable the language level access
control (see page 426). Of course this can only happen if the current security policy allows
itsee "Security" on page 677.

Let us suppose, for example, that you wanted to improve the Name class so that it didn't have to check
whether the cached hash code was valid each time. You could do this by setting hash in the constructor,
instead of lazily when it is asked for. But this causes a problem with serializationsince hash is transient it
does not get written as part of serialization (nor should it), so when you are deserializing you need to
explicitly set it. This means that you have to implement readObject to deserialize the main fields and then
set hash, which implies that you have to implement writeObject so that you know how the main fields

Free download pdf