CONTENTS | PREV | NEXT | Java Object Serialization Specification version 6.0 |
CHAPTER 2 |
Topics:
ObjectOutputStream
implements object serialization. It
maintains the state of the stream including the set of objects
already serialized. Its methods control the traversal of objects to
be serialized to save the specified objects and the objects to
which they refer.
package java.io; public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants { public ObjectOutputStream(OutputStream out) throws IOException; public final void writeObject(Object obj) throws IOException; public void writeUnshared(Object obj) throws IOException; public void defaultWriteObject() throws IOException, NotActiveException; public PutField putFields() throws IOException; public writeFields() throws IOException; public void reset() throws IOException; protected void annotateClass(Class cl) throws IOException; protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException; protected Object replaceObject(Object obj) throws IOException; protected boolean enableReplaceObject(boolean enable) throws SecurityException; protected void writeStreamHeader() throws IOException; public void write(int data) 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; protected void drain() throws IOException; public void close() throws IOException; public void writeBoolean(boolean data) throws IOException; public void writeByte(int data) throws IOException; public void writeShort(int data) throws IOException; public void writeChar(int data) throws IOException; public void writeInt(int data) throws IOException; public void writeLong(long data) throws IOException; public void writeFloat(float data) throws IOException; public void writeDouble(double data) throws IOException; public void writeBytes(String data) throws IOException; public void writeChars(String data) throws IOException; public void writeUTF(String data) throws IOException; // Inner class to provide access to serializable fields. abstract static public class PutField { public void put(String name, boolean value) throws IOException, IllegalArgumentException; public void put(String name, char data) throws IOException, IllegalArgumentException; public void put(String name, byte data) throws IOException, IllegalArgumentException; public void put(String name, short data) throws IOException, IllegalArgumentException; public void put(String name, int data) throws IOException, IllegalArgumentException; public void put(String name, long data) throws IOException, IllegalArgumentException; public void put(String name, float data) throws IOException, IllegalArgumentException; public void put(String name, double data) throws IOException, IllegalArgumentException; public void put(String name, Object data) throws IOException, IllegalArgumentException; } public void useProtocolVersion(int version) throws IOException; protected ObjectOutputStream() throws IOException; protected writeObjectOverride() throws NotActiveException, IOException; }The single-argument
ObjectOutputStream
constructor creates an
ObjectOutputStream
that serializes objects to the
given OutputStream
. The constructor calls
writeStreamHeader
to write a magic number and version
to the stream that will be read and verified by a corresponding
call to readStreamHeader
in the single-argument
ObjectInputStream
constructor. If a security manager
is installed, this constructor checks for the
"enableSubclassImplementation"
SerializablePermission
when invoked directly or
indirectly by the constructor of a subclass which overrides the
putFields
and/or writeUnshared
methods.
The
writeObject
method is used to serialize an object to
the stream. An object is serialized as follows:
1. | If a subclass is
overriding the implementation, call the
writeObjectOverride method and return. Overriding the
implementation is described at the end of this section. |
2. | If there is data in the block-data buffer, the data is written to the stream and the buffer is reset. |
3. | If the object is
null, null is put in the stream and writeObject
returns. |
4. | If the object has
been previously replaced, as described in Step 8, write the handle
of the replacement to the stream and writeObject
returns. |
5. | If the object has
already been written to the stream, its handle is written to the
stream and writeObject returns. |
6. | If the object is a
Class , the corresponding
ObjectStreamClass is written to the stream, a handle
is assigned for the class, and writeObject
returns. |
7. | If the object is an
ObjectStreamClass , a handle is assigned to the object,
after which it is written to the stream using one of the class
descriptor formats described in section 4.3. In versions 1.3 and later of the Java 2
SDK, Standard Edition, the writeClassDescriptor method
is called to output the ObjectStreamClass if it
represents a class that is not a dynamic proxy class, as determined
by passing the associated Class object to the
isProxyClass method of
java.lang.reflect.Proxy . Afterwards, an annotation for
the represented class is written: if the class is a dynamic proxy
class, then the annotateProxyClass method is called;
otherwise, the annotateClass method is called. The
writeObject method then returns. |
8. | Process potential
substitutions by the class of the object and/or by a subclass of
ObjectInputStream . |
a. | If the class of an
object is not an enum type and defines the appropriate
writeReplace method, the method is called. Optionally,
it can return a substitute object to be serialized. |
b. | Then, if enabled by
calling the enableReplaceObject method, the
replaceObject method is called to allow subclasses of
ObjectOutputStream to substitute for the object being
serialized. If the original object was replaced in the previous
step, the replaceObject method is called with the
replacement object. |
If the original object was replaced by either one or both steps above, the mapping from the original object to the replacement is recorded for later use in Step 4. Then, Steps 3 through 7 are repeated on the new object. |
If the replacement object is not one of the types covered by Steps 3 through 7, processing resumes using the replacement object at Step 10. |
9. | If the object is a
java.lang.String, the string is written as length
information followed by the contents of the string encoded in
modified UTF-8. For details, refer to Section 6.2, "Stream
Elements". A handle is assigned to the string, and
writeObject returns. |
10. | If the object is an
array, writeObject is called recursively to write the
ObjectStreamClass of the array. The handle for the
array is assigned. It is followed by the length of the array. Each
element of the array is then written to the stream, after which
writeObject returns. |
11. | If the object is an
enum constant, the ObjectStreamClass for the enum type
of the constant is written by recursively calling
writeObject . It will appear in the stream only the
first time it is referenced. A handle is assigned for the enum
constant. Next, the value returned by the name method
of the enum constant is written as a String object, as
described in step 9. Note that if the same name string has appeared
previously in the stream, a back reference to it will be written.
The writeObject method then returns. |
12. | For regular objects,
the ObjectStreamClass for the class of the object is
written by recursively calling writeObject . It will
appear in the stream only the first time it is referenced. A handle
is assigned for the object. |
13. | The contents of the object are written to the stream. |
a. | If the object is
serializable, the highest serializable class is located. For that
class, and each derived class, that class's fields are written.
If the class does not have a writeObject method, the
defaultWriteObject method is called to write the
serializable fields to the stream. If the class does have a
writeObject method, it is called. It may call
defaultWriteObject or putFields and
writeFields to save the state of the object, and then
it can write other information to the stream. |
b. | If the object is
externalizable, the writeExternal method of the object
is called. |
c. | If the object is
neither serializable or externalizable, the
NotSerializableException is thrown. |
Exceptions may occur during the traversal or may occur in the
underlying stream. For any subclass of IOException
,
the exception is written to the stream using the exception protocol
and the stream state is discarded. If a second
IOException
is thrown while attempting to write the
first exception into the stream, the stream is left in an unknown
state and StreamCorruptedException
is thrown from
writeObject
. For other exceptions, the stream is
aborted and left in an unknown and unusable state.
writeUnshared
method writes an "unshared" object to
the ObjectOutputStream
. This method is identical to
writeObject
, except that it always writes the given
object as a new, unique object in the stream (as opposed to a
back-reference pointing to a previously serialized instance).
Specifically:
writeUnshared
is always serialized in the same manner
as a newly appearing object (an object that has not been written to
the stream yet), regardless of whether or not the object has been
written previously.writeObject
is used to write an object that has been
previously written with writeUnshared
, the previous
writeUnshared
operation is treated as if it were a
write of a separate object. In other words,
ObjectOutputStream
will never generate back-references
to object data written by calls to writeUnshared
.writeUnshared
does not in itself guarantee a unique
reference to the object when it is deserialized, it allows a single
object to be defined multiple times in a stream, so that multiple
calls to the ObjectInputStream.readUnshared
method
(see Section 3.1, "The
ObjectInputStream Class") by the receiver will not
conflict. Note that the rules described above only apply to the
base-level object written with writeUnshared
, and not
to any transitively referenced sub-objects in the object graph to
be serialized.
The
defaultWriteObject
method implements the default
serialization mechanism for the current class. This method may be
called only from a class's writeObject
method. The
method writes all of the serializable fields of the current class
to the stream. If called from outside the writeObject
method, the NotActiveException
is thrown.
The
putFields
method returns a PutField
object the caller uses to set the values of the serializable fields
in the stream. The fields may be set in any order. After all of the
fields have been set, writeFields
must be called to
write the field values in the canonical order to the stream. If a
field is not set, the default value appropriate for its type will
be written to the stream. This method may only be called from
within the writeObject
method of a serializable class.
It may not be called more than once or if
defaultWriteObject
has been called. Only after
writeFields
has been called can other data be written
to the stream.
The reset
method resets the stream state to be the same as if it had just
been constructed. Reset
will discard the state of any
objects already written to the stream. The current point in the
stream is marked as reset, so the corresponding
ObjectInputStream
will reset at the same point.
Objects previously written to the stream will not be remembered as
already having been written to the stream. They will be written to
the stream again. This is useful when the contents of an object or
objects must be sent again. Reset
may not be called
while objects are being serialized. If called inappropriately, an
IOException
is thrown.
Starting with the Java 2
SDK, Standard Edition, v1.3, the writeClassDescriptor
method is called when an ObjectStreamClass
needs to be
serialized. writeClassDescriptor
is responsible for
writing a representation of the ObjectStreamClass
to
the serialization stream. Subclasses may override this method to
customize the way in which class descriptors are written to the
serialization stream. If this method is overridden, then the
corresponding readClassDescriptor
method in
ObjectInputStream
should also be overridden to
reconstitute the class descriptor from its custom stream
representation. By default, writeClassDescriptor
writes class descriptors according to the format specified in
Section 6.4, "Grammar for
the Stream Format". Note that this method will only be
called if the ObjectOutputStream
is not using the old
serialization stream format (see Section 6.3, "Stream Protocol
Versions"). If the serialization stream is using the old
format (ObjectStreamConstants.PROTOCOL_VERSION_1
), the
class descriptor will be written internally in a manner that cannot
be overridden or customized.
The
annotateClass
method is called while a
Class
is being serialized, and after the class
descriptor has been written to the stream. Subclasses may extend
this method and write other information to the stream about the
class. This information must be read by the
resolveClass
method in a corresponding
ObjectInputStream
subclass.
An
ObjectOutputStream
subclass can implement the
replaceObject
method to monitor or replace objects
during serialization. Replacing objects must be enabled explicitly
by calling enableReplaceObject
before calling
writeObject
with the first object to be replaced. Once
enabled, replaceObject
is called for each object just
prior to serializing the object for the first time. Note that the
replaceObject
method is not called for objects of the
specially handled classes, Class
and
ObjectStreamClass
. An implementation of a subclass may
return a substitute object that will be serialized instead of the
original. The substitute object must be serializable. All
references in the stream to the original object will be replaced by
the substitute object.
When objects are being
replaced, the subclass must ensure that the substituted object is
compatible with every field where the reference will be stored, or
that a complementary substitution will be made during
deserialization. Objects, whose type is not a subclass of the type
of the field or array element, will later abort the deserialization
by raising a ClassCastException
and the reference will
not be stored.
The
enableReplaceObject
method can be called by trusted
subclasses of ObjectOutputStream
to enable the
substitution of one object for another during serialization.
Replacing objects is disabled until
enableReplaceObject
is called with a true
value. It may thereafter be disabled by setting it to
false
. The previous setting is returned. The
enableReplaceObject
method checks that the stream
requesting the replacement can be trusted. To ensure that the
private state of objects is not unintentionally exposed, only
trusted stream subclasses may use replaceObject
.
Trusted classes are those classes that belong to a security
protection domain with permission to enable Serializable
substitution.
If the subclass of
ObjectOutputStream
is not considered part of the
system domain, SerializablePermission
"enableSubstitution"
must be added to the security
policy file. AccessControlException
is thrown if the
protection domain of the subclass of ObjectInputStream
does not have permission to
"enableSubstitution"
by calling
enableReplaceObject
. See the document Java Security
Architecture (JDK1.2) for additional information about the security
model.
The
writeStreamHeader
method writes the magic number and
version to the stream. This information must be read by the
readStreamHeader
method of
ObjectInputStream
. Subclasses may need to implement
this method to identify the stream's unique format.
The flush
method is used to empty any buffers being held by the stream and to
forward the flush to the underlying stream. The drain
method may be used by subclassers to empty only the
ObjectOutputStream
's buffers without forcing the
underlying stream to be flushed.
All of the write methods
for primitive types encode their values using a
DataOutputStream
to put them in the standard stream
format. The bytes are buffered into block data records so they can
be distinguished from the encoding of objects. This buffering
allows primitive data to be skipped if necessary for class
versioning. It also allows the stream to be parsed without invoking
class-specific methods.
To override the implementation of serialization, the subclass of
ObjectOutputStream
should call the protected no-arg
ObjectOutputStream
, constructor. There is a security
check within the no-arg constructor for
SerializablePermission
"enableSubclassImplementation"
to ensure that only
trusted classes are allowed to override the default implementation.
This constructor does not allocate any private data for
ObjectOutputStream
and sets a flag that indicates that
the final writeObject
method should invoke the
writeObjectOverride
method and return. All other
ObjectOutputStream
methods are not final and can be
directly overridden by the subclass.
Class PutField
provides the API for setting values
of the serializable fields for a class when the class does not use
default serialization. Each method puts the specified named value
into the stream. An IllegalArgumentException
is thrown
if name
does not match the name of a serializable
field for the class whose fields are being written, or if the type
of the named field does not match the second parameter type of the
specific put
method invoked.
writeObject
method allows a class to control the
serialization of its own fields. Here is its signature:
private void writeObject(ObjectOutputStream stream) throws IOException;Each subclass of a serializable object may define its own
writeObject
method. If a class does not implement the method, the default
serialization provided by defaultWriteObject
will be
used. When implemented, the class is only responsible for writing
its own fields, not those of its supertypes or subtypes.
The class's
writeObject
method, if implemented, is responsible for
saving the state of the class. Either
ObjectOutputStream
's
defaultWriteObject
or writeFields
method
must be called once (and only once) before writing any optional
data that will be needed by the corresponding
readObject
method to restore the state of the object;
even if no optional data is written,
defaultWriteObject
or writeFields
must
still be invoked once. If defaultWriteObject
or
writeFields
is not invoked once prior to the writing
of optional data (if any), then the behavior of instance
deserialization is undefined in cases where the
ObjectInputStream
cannot resolve the class which
defined the writeObject
method in question.
The responsibility for the format, structure, and versioning of the optional data lies completely with the class.
java.io.Externalizable
must implement the
writeExternal
method to save the entire state of the
object. It must coordinate with its superclasses to save their
state. All of the methods of ObjectOutput
are
available to save the object's primitive typed fields and
object fields.
public void writeExternal(ObjectOutput stream) throws IOException;
A new default format for writing Externalizable data has been
introduced in JDK 1.2. The new format specifies that primitive data
will be written in block data mode by writeExternal
methods. Additionally, a tag denoting the end of the External
object is appended to the stream after the
writeExternal
method returns. The benefits of this
format change are discussed in Section 3.6, "The readExternal
Method." Compatibility issues caused by this change are
discussed in Section 2.6, "The
useProtocolVersion Method."
writeReplace
method allows
a class of an object to nominate its own replacement in the stream
before the object is written. By implementing the
writeReplace
method, a class can directly control the
types and instances of its own instances being serialized.
The method is defined as follows:
ANY-ACCESS-MODIFIER Object writeReplace() { throws ObjectStreamException;
The writeReplace
method is called when
ObjectOutputStream
is preparing to write the object to
the stream. The ObjectOutputStream
checks whether the
class defines the writeReplace
method. If the method
is defined, the writeReplace
method is called to allow
the object to designate its replacement in the stream. The object
returned should be either of the same type as the object passed in
or an object that when read and resolved will result in an object
of a type that is compatible with all references to the object. If
it is not, a ClassCastException
will occur when the
type mismatch is discovered.
Stream protocol versions are discussed in Section 6.3, "Stream Protocol Versions."