objectoutputstream.java

来自「《移动Agent技术》一书的所有章节源代码。」· Java 代码 · 共 1,350 行 · 第 1/3 页

JAVA
1,350
字号
/*
 * @(#)ObjectOutputStream.java	1.35 98/02/05
 *
 * Copyright (c) 1995, 1996 Sun Microsystems, Inc. All Rights Reserved.
 *
 * This software is the confidential and proprietary information of Sun
 * Microsystems, Inc. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Sun.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
 * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
 * THIS SOFTWARE OR ITS DERIVATIVES.
 *
 * CopyrightVersion 1.1_beta
 */

package java.io;

import java.util.Stack;

import sun.io.ObjectOutputStreamDelegate; // RMI over IIOP hook.

/**
 * An ObjectOutputStream writes primitive data types and graphs of
 * Java objects to an OutputStream.  The objects can be read
 * (reconstituted) using an ObjectInputStream.
 * Persistent storage of objects can be accomplished by using a file for
 * the stream.
 * If the stream is a network socket stream, the objects can be reconsituted
 * on another host or in another process. <p>
 *
 * Only objects that support the java.io.Serializable interface can be
 * written to streams.
 *
 * The class of each serializable object is encoded including the class
 * name and signature of the class, the values of the
 * object's fields and arrays, and the closure of any other objects
 * referenced from the initial objects. <p>
 *
 * The method <STRONG>writeObject</STRONG> is used to write an object
 * to the stream.  Any object, including Strings and arrays, is
 * written with writeObject. Multiple objects or primitives can be
 * written to the stream.  The objects must be read back from the
 * corresponding ObjectInputstream with the same types and in the same
 * order as they were written.<p>
 *
 * Primitive data types can also be written to the stream using the
 * appropriate methods from DataOutput. Strings can also be written
 * using the writeUTF method.<p>
 *
 * The default serialization mechanism for an object writes the class
 * of the object, the class signature, and the values of all
 * non-transient and non-static fields.  References to other objects
 * (except in transient or static fields) cause those objects to be
 * written also. Multiple references to a single object are encoded
 * using a reference sharing mechanism so that graphs of objects can
 * be restored to the same shape as when the original was written. <p>
 *
 * For example to write an object that can be read by the example in ObjectInputStream: <br>
 * <PRE>
 *	FileOutputStream ostream = new FileOutputStream("t.tmp");
 *	ObjectOutputStream p = new ObjectOutputStream(ostream);
 *
 *	p.writeInt(12345);
 *	p.writeObject("Today");
 *	p.writeObject(new Date());
 *
 *	p.flush();
 *	ostream.close();
 *
 * </PRE>
 *
 * Classes that require special handling during the serialization and deserialization
 * process must implement special methods with these exact signatures: <p>
 *
 * <PRE>
 * private void readObject(java.io.ObjectInputStream stream)
 *     throws IOException, ClassNotFoundException;
 * private void writeObject(java.io.ObjectOutputStream stream)
 *     throws IOException
 * </PRE><p>
 * The writeObject method is responsible for writing the state of
 * the object for its particular class so that the corresponding
 * readObject method can restore it.
 * The method does not need to concern itself with the
 * state belonging to the object's superclasses or subclasses.
 * State is saved by writing the individual fields to the ObjectOutputStream
 * using the writeObject method or by using the methods for
 * primitive data types supported by DataOutput. <p>
 *
 * Serialization does not write out the fields of any object that does
 * not implement the java.io.Serializable interface.  Subclasses of
 * Objects that are not serializable can be serializable. In this case
 * the non-serializable class must have a no-arg constructor to allow
 * its fields to be initialized.  In this case it is the
 * responsibility of the subclass to save and restore the state of the
 * non-serializable class. It is frequently the case that the fields
 * of that class are accessible (public, package, or protected) or
 * that there are get and set methods that can be used to restore the
 * state. <p>
 *
 * Serialization of an object can be prevented by implementing writeObject
 * and readObject methods that throw the NotSerializableException.
 * The exception will be caught by the ObjectOutputStream and abort the
 * serialization process.
 *
 * Implementing the Externalizable interface allows the object to
 * assume complete control over the contents and format of the object's
 * serialized form.  The methods of the Externalizable interface,
 * writeExternal and readExternal, are called to save and restore the
 * objects state.  When implemented by a class they can write and read
 * their own state using all of the methods of ObjectOutput and
 * ObjectInput.  It is the responsibility of the objects to handle any
 * versioning that occurs.
 *
 * @author	Roger Riggs
 * @version     1.35, 02/05/98
 * @see java.io.DataOutput
 * @see java.io.ObjectInputStream
 * @see java.io.Serializable
 * @see java.io.Externalizable
 * @since       JDK1.1
 */
public class ObjectOutputStream
	extends OutputStream
	implements ObjectOutput, ObjectStreamConstants

{
    /**
     * Creates an ObjectOutputStream that writes to the specified OutputStream.
     * The stream header is written to the stream. The caller may want to call
     * flush immediately so that the corresponding ObjectInputStream can read
     * the header immediately.
     * @exception IOException Any exception thrown by the underlying OutputStream.
     * @since     JDK1.1
     */
    public ObjectOutputStream(OutputStream out) throws IOException {

        /*
         * RMI over IIOP hook. Check if we are a trusted subclass
         * that has implemented the "sun.io.ObjectOutputStream"
         * interface. If so, set our private flag that will be
         * checked in "writeObject", "defaultWriteObject" and
         * "enableReplaceObject". Note that we don't initialize
         * private instance variables in this case as an optimization
         * (subclasses using the hook should have no need for them).
         */

        if (this instanceof sun.io.ObjectOutputStreamDelegate && this.getClass().getClassLoader() == null) {
            isTrustedSubclass = true;
            return;
        }

	this.out = out;
	dos = new DataOutputStream(this);
	buf = new byte[1024];	// allocate buffer
	writeStreamHeader();
	resetStream();
    }

    /**
     * Write the specified object to the ObjectOutputStream.
     * The class of the object, the signature of the class, and the values
     * of the non-transient and non-static fields of the class and all
     * of its supertypes are written.  Default serialization for a class can be
     * overridden using the writeObject and the readObject methods.
     * Objects referenced by this object are written transitively so
     * that a complete equivalent graph of objects can be
     * reconstructed by an ObjectInputStream.  <p>
     *
     * Exceptions are thrown for
     * problems with the OutputStream and for classes that should not be
     * serialized.  All exceptions are fatal to the OutputStream, which
     * is left in an indeterminate state, and it is up to the caller
     * to ignore or recover the stream state.
     * @exception InvalidClassException Something is wrong with a class used by
     *	   serialization.
     * @exception NotSerializableException Some object to be serialized does not
     *	  implement the java.io.Serializable interface.
     * @exception IOException Any exception thrown by the underlying OutputStream.
     * @since     JDK1.1
     */
    public final void writeObject(Object obj)
	throws IOException
    {

	/*
	 * RMI over IIOP hook. Invoke delegate method if indicated.
	 */
	if (isTrustedSubclass) {
	    ((ObjectOutputStreamDelegate) this).writeObjectDelegate(obj);
	    return;
	}

	Object prevObject = currentObject;
	ObjectStreamClass prevClassDesc = currentClassDesc;
	boolean oldBlockDataMode = setBlockData(false);
	recursionDepth++;

	try {
	    if (serializeNullAndRepeat(obj))
		return;

	    if (checkSpecialClasses(obj))
		return;


	    /* If the replacment is enabled, give subclasses one chance
	     * to substitute a new object. If one is substituted,
	     * recheck for null, repeated refs, and special cased classes
	     */
	    if (enableReplace) {
		Object altobj = replaceObject(obj);
		if (obj != altobj) {

		    if (altobj != null && !(altobj instanceof Serializable)) {
			String clname = altobj.getClass().getName();
			throw new NotSerializableException(clname);
		    }

		    // If the alternate object is already
		    // serialized just remember the replacement
		    if (serializeNullAndRepeat(altobj)) {
			addReplacement(obj, altobj);
			return;
		    }

		    /* Add this to the set of replaced objects.
		     * This must be done before the object is
		     * serialized so that if the object indirectly
		     * refers to the original it will be redirected to
		     * the replacement.
		     *
		     * NB: checkSpecialClasses should only call
		     * serializeNullandRepeat for objects that will not
		     * recurse.
		     */
		    addReplacement(obj, altobj);

		    if (checkSpecialClasses(altobj))
			return;

		    obj = altobj;
		}
	    }
	    if (checkSubstitutableSpecialClasses(obj))
		return;
	    else {
		/* Write out the object as itself */
		outputObject(obj);
	    }
	} catch (ObjectStreamException ee) {
	    if (abortIOException == null) {
		try {
		    /* Prepare to write the exception to the stream.
		     * End blockdatamode in case it's set
		     * Write the exception code
		     * reset the stream to forget all previous objects
		     * write the exception that occurred
		     * reset the stream again so subsequent objects won't map to
		     * the exception or its args.
		     * Continue below to rethrow the exception.
		     */
		    setBlockData(false);

		    writeCode(TC_EXCEPTION);
		    resetStream();
		    this.writeObject(ee);
		    resetStream();

		    // Set the pending exception to be rethrown.
		    abortIOException = ee;
		} catch (IOException fatal) {
		    /* An exception occurred while writing the original exception to
		     * the stream.  The original exception is not complete in
		     * the stream and recusion would be bad. Supercede the original
		     * Exception with a StreamCorruptedException using the message
		     * from this current exception.
		     */
		    abortIOException =
			new StreamCorruptedException(fatal.getMessage());
		}
	    }
	} catch (IOException ee) {
	    // Don't supercede a pending exception, the original will be re-thrown.
	    if (abortIOException == null)
		abortIOException = ee;

	} finally {
	    /* Restore state of previous call incase this is a nested call */
	    recursionDepth--;
	    currentObject = prevObject;
	    currentClassDesc = prevClassDesc;
	    setBlockData(oldBlockDataMode);
	}

	/* If the recursion depth is 0, test for and clear the pending exception.
	 * If there is a pending exception throw it.
	 */
	IOException pending = abortIOException;
	if (recursionDepth == 0)
	    abortIOException = null;
	if (pending != null) {
	    throw pending;
	}
    }

    /*
     * Check for special cases of serializing objects.
     */
    private boolean checkSpecialClasses(Object obj) throws IOException {

	/*
	 * If this is a class, don't allow substitution
	 */
	if (obj instanceof Class) {
	    outputClass((Class)obj);
	    return true;
	}

	if (obj instanceof ObjectStreamClass) {
	    outputClassDescriptor((ObjectStreamClass)obj);
	    return true;
	}

	return false;
    }

    /*
     * Check for special cases of substitutable serializing objects.
     * These classes are replaceable.
     */
    private boolean checkSubstitutableSpecialClasses(Object obj)
	throws IOException
    {
	if (obj instanceof String) {
	    outputString((String)obj);
	    return true;
	}

	if (obj.getClass().isArray()) {
	    outputArray(obj);
	    return true;
	}

	return false;
    }

    /**
     * Write the non-static and non-transient fields of the current class
     * to this stream.  This may only be called from the writeObject method
     * of the class being serialized. It will throw the NotActiveException
     * if it is called otherwise.
     * @since     JDK1.1
     */
    public final void defaultWriteObject() throws IOException {

	/*
	 * RMI over IIOP hook. Invoke delegate method if indicated.
	 */
	if (isTrustedSubclass) {
	    ((ObjectOutputStreamDelegate) this).defaultWriteObjectDelegate();
	    return;
	}

	if (currentObject == null || currentClassDesc == null)
	    throw new NotActiveException("defaultWriteObject");

	if (currentClassDesc.getFieldSequence() != null) {
	    boolean prevmode = setBlockData(false);
	    outputClassFields(currentObject, currentClassDesc.forClass(),
			      currentClassDesc.getFieldSequence());
	    setBlockData(prevmode);
	}
    }

    /**
     * Reset will disregard the state of any objects already written
     * to the stream.  The state is reset to be the same as a new
     * ObjectOutputStream.  The current point in the stream is marked
     * as reset so the corresponding ObjectInputStream will be reset
     * at the same point.  Objects previously written to the stream
     * will not be refered to as already being in the stream.  They
     * will be written to the stream again.
     * @since     JDK1.1
     */
    public void reset() throws IOException {
	if (currentObject != null || currentClassDesc != null)
	    throw new IOException("Illegal call to reset");

	/* Write a reset to the stream. */
	setBlockData(false);
	writeCode(TC_RESET);

	resetStream();			// re-init the stream
	abortIOException = null;
    }

    /*
     * Internal reset function to reinitialize the state of the stream.
     * Reset state of things changed by using the stream.
     */
    private void resetStream() throws IOException {
	if (wireHandle2Object == null) {
	    wireHandle2Object = new Object[100];
	    wireNextHandle = new int[100];
	    wireHash2Handle = new int[101];
	} else {

	    // Storage Optimization for frequent calls to reset method.
	    // Do not reallocate, only reinitialize.
	    for (int i = 0; i < nextWireOffset; i++) {
		wireHandle2Object[i] = null;
		wireNextHandle[i] = 0;
	    }
	}
  	nextWireOffset = 0;
  	for (int i = 0; i < wireHash2Handle.length; i++) {
  	    wireHash2Handle[i] = -1;
  	}
	if (classDescStack == null)
	    classDescStack = new Stack();
	else
	    classDescStack.setSize(0);

	for (int i = 0; i < nextReplaceOffset; i++)
	    replaceObjects[i] = null;
	nextReplaceOffset = 0;
	setBlockData(true);		/* Re-enable buffering */
    }

    /**
     * Subclasses may implement this method to allow class data to be stored
     * in the stream. By default this method does nothing.
     * The corresponding method in ObjectInputStream is resolveClass.
     * This method is called exactly once for each unique class in the stream.
     * The class name and signature will have already been written to the stream.
     * This method may make free use of the ObjectOutputStream to save
     * any representation of the class it deems suitable (for example,
     * the bytes of the class file).  The resolveClass method in the corresponding
     * subclass of ObjectInputStream must read and use any data or objects
     * written by annotateClass.
     * annotateClass is called only for normal classes.  Arrays are not normal classes.
     * @exception IOException Any exception thrown by the underlying OutputStream.
     * @since     JDK1.1

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?