Objects to be stored and retrieved frequently refer to other objects. Those other objects must be stored and retrieved at the same time to maintain the relationships between the objects. When an object is stored all of the objects that are reachable from that object are stored as well.
The goals for serializing Java objects are to:
// Serialize today's date to a file. FileOutputStream f = new FileOutputStream("tmp"); ObjectOutput s = new ObjectOutputStream(f); s.writeObject("Today"); s.writeObject(new Date()); s.flush();
OutputStream
, in this case a FileOutputStream
, is needed to receive the bytes. Then an ObjectOutputStream
is created that writes to the OutputStream. Next, the string "Today" and a Date object are written to the stream. More generally, objects are written with the writeObject
method and primitives are written to the stream with the methods of DataOutput
.The
writeObject
method serializes the specified object and traverses its references to other objects in the object graph recursively to create a complete serialized representation of the graph. Within a stream, the first reference to any object results in the object being serialized or externalized and the assignment of a handle for that object. Subsequent references to that object are encoded as the handle. Using object handles preserves sharing and circular references that occur naturally in object graphs. Subsequent references to an object use only the handle allowing a very compact representation.Special handling is required for objects of type
Class
, ObjectStreamClass
, strings, and arrays. Other objects must implement either Serializable or Externalizable interfaces to be saved in or restored from a stream. Primitive data types are written to the stream with the methods in the
DataOutput
interface, such as writeInt
, writeFloat
, or writeUTF
. Individual bytes and arrays of bytes are written with the methods of OutputStream
. All primitive data is written to the stream in block-data records prefixed by a marker and the length. Putting the data in records allows it to be skipped if necessary.
ObjectOutputStream
can be extended to customize the information about classes in the stream or to replace objects to be serialized. Refer to the annotateClass
and replaceObject
method descriptions for details.
// Deserialize a string and date from a file. FileInputStream in = new FileInputStream("tmp"); ObjectInputStream s = new ObjectInputStream(in); String today = (String)s.readObject(); Date date = (Date)s.readObject();
InputStream
, in this case a FileInputStream
, is needed as the source stream. Then an ObjectInputStream
is created that reads from the InputStream
. Next, the string "Today" and a Date object are read from the stream. More generally, objects are read with the readObject
method and primitives are read from the stream with the methods of DataInput
.The
readObject
method deserializes the next object in the stream and traverses its references to other objects recursively to create the complete graph of objects serialized.Primitive data types are read from the stream with the methods in the
DataOutput
interface, such as readInt
, readFloat
, or readUTF
. Individual bytes and arrays of bytes are read with the methods of InputStream
. All primitive data is read from block-data records.ObjectInputStream can be extended to utilize customized information in the stream about classes or to replace objects that have been deserialized. Refer to the
resolveClass
and resolveObject
method descriptions for details.
Each object acting as a container implements an interface that allows primitives and objects to be stored in or retrieved from it. These are the
ObjectOutput
and ObjectInput
interfaces which:
For a Serializable class, Object Serialization can automatically save and restore fields of each class of an object and automatically handle classes that evolve by adding fields or supertypes. A Serializable class can declare which of its fields are transient (not saved or restored), and write and read optional values and objects.
For an Externalizable class, Object Serialization delegates to the class complete control over its external format and how the state of the supertype is saved and restored.
ObjectOutput
interface provides an abstract stream based interface to object storage. It extends DataOutput so those methods may be used for writing primitive data types. Objects implementing this interface can be used to store primitives and objects.
package java.io; public interface ObjectOutput extends DataOutput { public void writeObject(Object obj) throws IOException; public void write(int b) throws IOException; public void write(byte b[]) throws IOException; public void write(byte b[], int off, int len) throws IOException; public void flush() throws IOException; public void close() throws IOException; }
writeObject
method is used to write an object. The exceptions thrown reflect errors while accessing the object or its fields, or exceptions that occur in writing to storage. If any exception is thrown, the underlying storage may be corrupted, refer to the object implementing this interface for details.
ObjectInput
interface provides an abstract stream based interface to object retrieval. It extends DataInput
so those methods for reading primitive data types are accessible in this interface.
package java.io; public interface ObjectInput extends DataInput { public Object readObject() throws ClassNotFoundException, IOException; public int read() throws IOException; public int read(byte b[]) throws IOException; public int read(byte b[], int off, int len) throws IOException; public long skip(long n) throws IOException; public int available() throws IOException; public void close() throws IOException; }
readObject
method is used to read and return an object. The exceptions thrown reflect errors while accessing the objects or its fields or exceptions that occur in reading from the storage. If any exception is thrown, the underlying storage may be corrupted, refer to the object implementing this interface for details.
package java.io; public interface Serializable {};
Serializable
object:
java.io.Serializable
interface.
writeObject
method to control what information is saved or to append additional information to the stream.
readObject
method so it can read the information written by the corresponding writeObject
method or to update the state of the object after it has been restored.
ObjectOutputStream
and ObjectInputStream
are designed and implemented to allow the Serializable classes they operate on to evolve, that is, to allow changes to the classes that are compatible with the earlier versions of the classes. Details of the mechanism to allow compatible changes can be found in Compatible Java Type Evolution.
The Externalizable Interface
For Externalizable objects only the identity of class of the object is saved by the container and it is the responsibility of the class to save and restore the contents. The interface Externalizable is defined as:
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; }
Externalizable
Object:
java.io.Externalizable
interface.
writeExternal
method to save the state of the object. It must explicitly coordinate with its supertype to save its state.
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 save its state.
writeExternal
and readExternal
methods are solely responsible for that format.
writeExternal
and readExternal methods are public and raise 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 it would not present a security risk.
The easiest technique 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 at all. To accomplish this the object should not implement either the Serializable or Externalizable interfaces.
Some classes may find it beneficial to allow writing and reading but 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.
Copyright © 1996 Sun Microsystems, Inc., 2550 Garcia Ave., Mtn. View, CA 94043-1100 USA. All rights reserved.