CONTENTS | PREV | NEXT | Java Object Serialization Specification version 6.0 |
CHAPTER 1
Serializable
or the
Externalizable
interface. For JavaTM
objects, the serialized form must be able to identify and verify
the JavaTM class from which the contents of the object
were saved and to restore the contents to a new instance. For
serializable objects, the stream includes sufficient information to
restore the fields in the stream to a compatible version of the
class. For Externalizable objects, the class is solely responsible
for the external format of its contents.
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 JavaTM 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();First an
OutputStream
, in this case a
FileOutputStream
, is needed to receive the bytes. Then
an ObjectOutputStream
is created that writes to the
FileOutputStream
. 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 (see Section 2.3, "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 arrays, enum constants, and objects of type
Class
, ObjectStreamClass
, and
String
. Other objects must implement either the
Serializable
or the Externalizable
interface 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
. Except for serializable fields,
primitive data is written to the stream in block-data records, with
each record prefixed by a marker and an indication of the number of
bytes in the record.
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();First an
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. 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 DataInput
interface, such as readInt
, readFloat
, or
readUTF
. Individual bytes and arrays of bytes are read
with the methods of InputStream
. Except for
serializable fields, 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 that acts as
a container implements an interface which allows primitives and
objects to be stored in or retrieved from it. These interfaces are
the ObjectOutput
and ObjectInput
interfaces which:
To be stored in an
Object Stream, each object must implement either the
Serializable
or the Externalizable
interface:
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 saved or restored, and write and read optional values and
objects.Externalizable
class, Object Serialization delegates
to the class complete control over its external format and how the
state of the supertype(s) is saved and restored.Serializable
class,
serialPersistentFields
. This field must be initialized
with an array of ObjectStreamField
objects that list
the names and types of the serializable fields. The modifiers for
the field are required to be private, static, and final. If the
field's value is null or is otherwise not an instance of
ObjectStreamField[]
, or if the field does not have the
required modifiers, then the behavior is as if the field were not
declared at all.
For example, the following declaration duplicates the default behavior.
class List implements Serializable { List next; private static final ObjectStreamField[] serialPersistentFields = {new ObjectStreamField("next", List.class)};
}By using
serialPersistentFields
to define the Serializable
fields for a class, there no longer is a limitation that a
serializable field must be a field within the current definition of
the Serializable
class. The writeObject
and readObject
methods of the
Serializable
class can map the current implementation
of the class to the serializable fields of the class using the
interface that is described in Section 1.7, "Accessing
Serializable Fields of a Class." Therefore, the fields for
a Serializable
class can change in a later release, as
long as it maintains the mapping back to its Serializable fields
that must remain compatible across release boundaries.
serialPersistentFields
for an inner class (though it
is possible to set it for static member classes). For other
restrictions pertaining to serialization of inner class instances,
see section Section 1.10,
"The Serializable Interface".
@serial
,
@serialField
, and @serialData
, provide a
way to document the serialized form for a Serializable class within
the source code.
@serial
tag should be placed in the javadoc comment
for a default serializable field. The syntax is as follows:
@serial
field-description The optional
field-description describes the meaning of the field and
its acceptable values. The field-description can span
multiple lines. When a field is added after the initial release, a
@since tag indicates the version the field was added. The
field-description for @serial
provides
serialization-specific documentation and is appended to the javadoc
comment for the field within the serialized form
documentation.@serialField
tag is used to document an
ObjectStreamField
component of a
serialPersistentFields
array. One of these tags should
be used for each ObjectStreamField
component. The
syntax is as follows: @serialField
field-name
field-type field-description@serialData
tag describes the sequences and types of
data written or read. The tag describes the sequence and type of
optional data written by writeObject
or all data
written by the Externalizable.writeExternal
method.
The syntax is as follows: @serialData
data-description When a class is declared
Serializable, the serializable state of the object is defined by
serializable fields (by name and type) plus optional data. Optional
data can only be written explicitly by the writeObject
method of a Serializable
class. Optional data can be
read by the Serializable
class'
readObject
method or serialization will skip unread
optional data.
When a class is declared Externalizable, the data that is written to the stream by the class itself defines the serialized state. The class must specify the order, types, and meaning of each datum that is written to the stream. The class must handle its own evolution, so that it can continue to read data written by and write data that can be read by previous versions. The class must coordinate with the superclass when saving and restoring data. The location of the superclasses data in the stream must be specified.
The designer of a Serializable class must ensure that the information saved for the class is appropriate for persistence and follows the serialization-specified rules for interoperability and evolution. Class evolution is explained in greater detail in Chapter 5, "Versioning of Serializable Objects."
Serializable
interface and do no further
customization. The serializable fields are mapped to the
corresponding fields of the class and values are either written to
the stream from those fields or are read in and assigned
respectively. If the class provides writeObject
and
readObject
methods, the default mechanism can be
invoked by calling defaultWriteObject
and
defaultReadObject
. When the writeObject
and readObject
methods are implemented, the class has
an opportunity to modify the serializable field values before they
are written or after they are read.
When the default
mechanism cannot be used, the serializable class can use the
putFields
method of ObjectOutputStream
to
put the values for the serializable fields into the stream. The
writeFields
method of ObjectOutputStream
puts the values in the correct order, then writes them to the
stream using the existing protocol for serialization.
Correspondingly, the readFields
method of
ObjectInputStream
reads the values from the stream and
makes them available to the class by name in any order. See
Section 2.2, "The
ObjectOutputStream.PutField Class" and Section 3.2, "The
ObjectInputStream.GetField Class." for a detailed
description of the Serializable Fields API.
ObjectOutput
interface provides an abstract,
stream-based interface to object storage. It extends the DataOutput
interface so those methods can be used for writing primitive data
types. Objects that implement 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; }
The
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. If
this occurs, refer to the object that is implementing this
interface for more information.
ObjectInput
interface provides an abstract stream
based interface to object retrieval. It extends the
DataInput
interface 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; }The
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. If this occurs,
refer to the object implementing this interface for additional
information.
Serializable
interface is
defined to identify classes which implement the serializable
protocol:
package java.io; public interface Serializable {};A Serializable class must do the following:
serialPersistentFields
member to explicitly declare
them serializable or use the transient keyword to denote
nonserializable fields.)writeObject
method to control what information is
saved or to append additional information to the streamreadObject
method either to read the information
written by the corresponding writeObject
method or to
update the state of the object after it has been restoredwriteReplace
method to allow a class to nominate a
replacement object to be written to the streamreadResolve
method to allow a class to designate a
replacement object for the object just read from the streamObjectOutputStream
and ObjectInputStream
allow the serializable classes on which they operate to evolve
(allow changes to the classes that are compatible with the earlier
versions of the classes). See Section 5.5, "Compatible
JavaTM Type Evolution" for information about the
mechanism which is used to allow compatible changes.
javac
(or other
JavaTM compilers) to implement inner classes are
implementation dependent and may vary between compilers;
differences in such fields can disrupt compatibility as well as
result in conflicting default serialVersionUID
values.
The names assigned to local and anonymous inner classes are also
implementation dependent and may differ between compilers. Since
inner classes cannot declare static members other than compile-time
constant fields, they cannot use the
serialPersistentFields
mechanism to designate
serializable fields. Finally, because inner classes associated with
outer instances do not have zero-argument constructors
(constructors of such inner classes implicitly accept the enclosing
instance as a prepended parameter), they cannot implement
Externalizable
. None of the issues listed above,
however, apply to static member classes.
Externalizable
interface is defined as follows:
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 class of an Externalizable object must do the following:
java.io.Externalizable
interfacewriteExternal
method to save the state of the
objectreadExternal
method to read the data written by the
writeExternal
method from the stream and restore the
state of the objectwriteExternal
and readExternal
methods be
solely responsible for the format, if an externally defined format
is writtenwriteExternal
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 does not present
a security risk.
Externalizable
interface mechanism cannot be used for
inner classes and they should implement the
Serializable
interface, if they must be serialized.
Several limitations exist for serializable inner classes as well,
however; see Section 1.10,
"The Serializable Interface", for a full enumeration.
readResolve
method to allow a class to designate a
replacement object for the object just read from the streamObjectOutputStream
writes the value returned by the enum constant's
name
method. To deserialize an enum constant,
ObjectInputStream
reads the constant name from the
stream; the deserialized constant is then obtained by calling the
java.lang.Enum.valueOf
method, passing the
constant's enum type along with the received constant name as
arguments. Like other serializable or externalizable objects, enum
constants can function as the targets of back references appearing
subsequently in the serialization stream.
The process by which
enum constants are serialized cannot be customized: any
class-specific writeObject
, readObject
,
readObjectNoData
, writeReplace
, and
readResolve
methods defined by enum types are ignored
during serialization and deserialization. Similarly, any
serialPersistentFields
or
serialVersionUID
field declarations are also
ignored--all enum types have a fixed serialVersionUID
of 0L
. Documenting serializable fields and data for
enum types is unnecessary, since there is no variation in the type
of data sent.
The easiest technique is to mark fields that contain sensitive data as private transient. Transient fields are not persistent and will not be saved by any persistence mechanism. 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 transient fields of the class are safe.
Particularly sensitive
classes should not be serialized at all. To accomplish this, the
object should not implement either the Serializable
or
the Externalizable
interface.
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.