| |
|
|
Trail: Essential Java Classes
|
Lesson: Reading and Writing (but no 'rithmetic)
|
|
Providing Object Serialization for Your Classes
An object is serializable only
if its class implements the Serializable
interface. Thus, if you want to serialize the instances of one of your
classes, the class must implement the Serializable interface.
The good news is that Serializable is an empty interface.
That is, it doesn't
contain any method declarations; it's purpose is simply to identify
classes whose objects are serializable.
Implementing the Serializable Interface
Here's the complete definition of the Serializable interface:
package java.io;
public interface Serializable {
// there's nothing in here!
};
Making instances of your classes serializable is easy.
You just add the implements Serializable clause
to your class declaration like this:
public class MySerializableClass implements Serializable {
...
}
You don't have to write any methods.
The serialization of instances of this class
are handled by the defaultWriteObject method
of ObjectOutputStream.
This method automatically writes out everything
required to reconstruct an instance of the class,
including the following:
-
Class of the object
-
Class signature
-
Values of all non-
transient and non-static members,
including members that refer to other objects
For many classes, this default behavior is good enough. However,
default serialization can be slow, and a class might want more explicit
control over the serialization.
Customizing Serialization
You can customize serialization for your classes by providing two
methods for it: writeObject and readObject.
The writeObject method
controls what information is saved. It is typically used to append
additional information to the stream. The readObject method either
reads the information written
by the corresponding writeObject method
or can be used to update the state of the object after it has been
restored.
The writeObject method must be declared exactly
as shown in the following example.
Also, it should call the stream's defaultWriteObject
as the first thing it does to perform default serialization.
Any special arrangements can be handled afterwards:
private void writeObject(ObjectOutputStream s)
throws IOException {
s.defaultWriteObject();
// customized serialization code
}
The readObject method must read in everything
written by writeObject in the same order
in which it was written.
Also, the readObject method can perform calculations
or update the state of the object in some way.
Here's the readObject method that corresponds
to the writeObject method just shown:
private void readObject(ObjectInputStream s)
throws IOException {
s.defaultReadObject();
// customized deserialization code
...
// followed by code to update the object, if necessary
}
The readObject method must be declared exactly as shown.
The writeObject and readObject methods are
responsible for serializing only the immediate class. Any serialization
required by the superclasses is handle automatically. However, a class
that needs to explicitly coordinate with its superclasses to serialize
itself can do so by implementing the Externalizable interface.
Implementing the Externalizable Interface
For complete, explicit control of the serialization process, a class
must implement the Externalizable interface.
For Externalizable
objects, only the identity of the object's class is automatically saved
by the stream. The class is responsible for writing and reading its
contents, and it must coordinate with its superclasses to do so.
Here's the complete definition of the Externalizable
interface that extends Serializable:
package java.io;
public interface Externalizable extends Serializable
{
public void writeExternal(ObjectOutput out)
throws IOException;
public void readExternal(ObjectInput in)
throws IOException,
java.lang.ClassNotFoundException;
}
The following holds for an Externalizable class:
-
It must implement the
java.io.Externalizable interface.
-
It must implement a
writeExternal method to save the state of the
object. Also, it must explicitly coordinate with its supertype to save
its state.
-
It must implement a
readExternal method to read the data
written by the writeExternal method from the stream and
restore the state of the object. It must explicitly coordinate with the
supertype to restore its state.
-
If externally defined format is being written,
the
writeExternal and readExternal
methods are solely responsible for that format.
The writeExternal and readExternal methods
are public and carry the risk that a client may be able to write or
read information in the object other than by using its methods and
fields. These methods must be used only when the information held by
the object is not sensitive or when exposing that information would not
present a security risk.
Protecting Sensitive Information
When developing a class that provides controlled access to resources,
you must take care to protect sensitive information and functions.
During deserialization, the private state of the object is restored.
For example, a file descriptor contains a handle that provides access
to an operating system resource. Being able to forge a file descriptor
would allow some forms of illegal access, since restoring state is done
from a stream. Therefore the serializing runtime must take the
conservative approach and not trust the stream to contain only valid
representations of objects. To avoid compromising a class, you must
provide either that the sensitive state of an object must not be
restored from the stream or that it must be reverified by the class.
Several techniques are available to protect sensitive data in classes.
The easiest is to mark fields that contain sensitive data as private
transient.
transient and static fields are not serialized or
deserialized. Marking the field will prevent the state from appearing
in the stream and from being restored during deserialization. Since
writing and reading (of private fields) cannot be superseded outside of
the class, the class's transient fields are safe.
Particularly sensitive classes should not be serialized. To accomplish
this, the object should not implement either the Serializable
or Externalizable interface.
Some classes may find it beneficial to allow writing and reading but to
specifically handle and revalidate the state as it is deserialized. The
class should implement writeObject
and readObject methods to save and
restore only the appropriate state. If access should be denied,
throwing a NotSerializableException will prevent further access.
|
|
|
|
| |
Michel RIVEILL

Laboratoire I3S - Bât. ESSI
930 Route des Colles
06903 Sophia Antipolis CEDEX
email :
riveill at unice.fr
Généralité
Ressources en lignes
Les rubriques des cours :
dernière mise à jour
le 18 septembre 2003
|