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.