objectoutputstream.java

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

JAVA
1,350
字号
     */
    protected void annotateClass(Class cl)
	throws IOException
    {
    }

    /** This method will allow trusted subclasses of ObjectOutputStream
     * to substitute one object for another during
     * serialization. Replacing objects is disabled until
     * enableReplaceObject is called. The enableReplaceObject method
     * checks that the stream requesting to do replacment can be
     * trusted. Every reference to serializable objects is passed to
     * replaceObject.  To insure that the private state of objects is
     * not unintentionally exposed only trusted streams may use
     * replaceObject. <p>
     *
     * When a subclass is replacing objects it must insure that either
     * a complementary substitution must be made during
     * deserialization or that the substituted object is compatible
     * with every field where the reference will be stored.  Objects
     * whose type is not a subclass of the type of the field or array
     * element abort the serialization by raising an exception and the
     * object is not be stored. <p>
     *
     * This method is called only once when each object is first encountered.
     * All subsequent references to the object will be redirected to the
     * new object. This method should return the object to be substituted or
     * the original object. <P>
     *
     * Null can be returned as the object to be substituted, but may
     * cause NullReferenceException in classes that contain references
     * to the original object since they may be expecting an object
     * instead of null.<p>
     *
     * @exception IOException Any exception thrown by the underlying
     * OutputStream.
     * @since     JDK1.1
     */
    protected Object replaceObject(Object obj)
	throws IOException
    {
	return obj;
    }

    /**
     * Enable the stream to do replacement of objects in the stream.
     * If the stream is a trusted class it is allowed to enable replacement.
     * Trusted classes are those classes with a classLoader equals null. <p>
     *
     * When enabled the replaceObject method is called for every object
     * being serialized.
     *
     * @exception SecurityException The classloader of this stream object is non-null.
     * @since     JDK1.1
     */
    protected final boolean enableReplaceObject(boolean enable)
	throws SecurityException
    {

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

	boolean previous = enableReplace;
	if (enable) {
	    ClassLoader loader = this.getClass().getClassLoader();
	    if (loader == null) {
		enableReplace = true;
		return previous;
	    }
	    throw new SecurityException("Not trusted class");
	} else {
	    enableReplace = false;
	}
	return previous;
    }

    /**
     * The writeStreamHeader method is provided so subclasses can
     * append or prepend their own header to the stream.
     * It writes the magic number and version to the stream.
     * @since     JDK1.1
     */
    protected void writeStreamHeader() throws IOException {
	writeShort(STREAM_MAGIC);
	writeShort(STREAM_VERSION);
    }

    /**
     * Write a string to the stream.
     * Note that since Strings are Objects, writeObject
     * will behave identically.
     */
    private void outputString(String s) throws IOException {
	/* Allocate a write handle but don't write it to the stream,
	 * Write out the code for a string,
	 * the read can regenerate the same sequence and it saves bytes.
	 */
	assignWireOffset(s);
	writeCode(TC_STRING);
	writeUTF(s);
    }


    /* Classes are special, they can not created during deserialization,
     * but the appropriate class can be found.
     */
    private void outputClass(Class aclass) throws IOException {

	writeCode(TC_CLASS);
	/* Find the class descriptor and write it out */
	ObjectStreamClass v = ObjectStreamClass.lookup(aclass);

	if (v == null)
	    throw new NotSerializableException(aclass.getName());

	outputClassDescriptor(v);

	assignWireOffset(aclass);
    }


    /* Write the class descriptor */
    private void outputClassDescriptor(ObjectStreamClass classdesc)
	throws IOException
    {
	if (serializeNullAndRepeat(classdesc))
	    return;

	/* Write out the code for a class
	 * Write out the class name and its serialVersionUID
	 */
	writeCode(TC_CLASSDESC);
	String classname = classdesc.getName();

	writeUTF(classname);
	writeLong(classdesc.getSerialVersionUID());

	/* This is done here to be symetric with the inputClass method
	 * Since the resolveClassName() method may use the stream.
	 * The assignments of wirehandles must be done in the same order
	 */
	assignWireOffset(classdesc);

	/* Write the version description for this class */
	classdesc.write(this);

	/* Give subclassers a chance to add the class implementation
	 * to the stream.  Set BlockData mode so any information they
	 * write can be skipped on reading.
	 */
	boolean prevMode = setBlockData(true);
	annotateClass(classdesc.forClass());
	setBlockData(prevMode);
	writeCode(TC_ENDBLOCKDATA);

	/*
	 * Write out the superclass descriptor of this descriptor
	 * only if it is for a java.io.Serializable class.
	 * else write null.
	 */
	ObjectStreamClass superdesc = classdesc.getSuperclass();
	outputClassDescriptor(superdesc);
    }

    /**
     * Write an array out. Note that since Arrays are Objects, writeObject(obj)
     * will behave identically. <br><br>
     * @param o can represent an array of any type/dimension.
     */
    private void outputArray(Object obj)
	throws IOException
    {
	Class currclass = obj.getClass();

	ObjectStreamClass v = ObjectStreamClass.lookup(currclass);

	/* Write out the code for an array and the name of the class */
	writeCode(TC_ARRAY);
	outputClassDescriptor(v);

	/* Assign the wirehandle for this object and outputArrayValues
	 * writes the length and the array contents.
	 */
	assignWireOffset(obj);

	int i, length;
	Class type = currclass.getComponentType();

	if (type.isPrimitive()) {
	    /* Write arrays of primitive types using the DataOutput
	     * methods that convert each element into the output buffer.
	     * The data types are ordered by the frequency
	     * in which they are expected to occur.
	     */
	    if (type == Integer.TYPE) {
		int[] array = (int[])obj;
		length = array.length;
		writeInt(length);
		for (i = 0; i < length; i++) {
		    writeInt(array[i]);
		}
	    } else if (type == Byte.TYPE) {
		byte[] array = (byte[])obj;
		length = array.length;
		writeInt(length);
		writeInternal(array, 0, length, true);
	    } else if (type == Long.TYPE) {
		long[] array = (long[])obj;
		length = array.length;
		writeInt(length);
		for (i = 0; i < length; i++) {
		    writeLong(array[i]);
		}
	    } else if (type == Float.TYPE) {
		float[] array = (float[])obj;
		length = array.length;
		writeInt(length);
		for (i = 0; i < length; i++) {
		    writeFloat(array[i]);
		}
	    } else if (type == Double.TYPE) {
		double[] array = (double[])obj;
		length = array.length;
		writeInt(length);
		for (i = 0; i < length; i++) {
		    writeDouble(array[i]);
		}
	    } else if (type == Short.TYPE) {
		short[] array = (short[])obj;
		length = array.length;
		writeInt(length);
		for (i = 0; i < length; i++) {
		    writeShort(array[i]);
		}
	    } else if (type == Character.TYPE) {
		char[] array = (char[])obj;
		length = array.length;
		writeInt(length);
		for (i = 0; i < length; i++) {
		    writeChar(array[i]);
		}
	    } else if (type == Boolean.TYPE) {
		boolean[] array = (boolean[])obj;
		length = array.length;
		writeInt(length);
		for (i = 0; i < length; i++) {
		    writeBoolean(array[i]);
		}
	    } else {
		throw new InvalidClassException(currclass.getName());
	    }
	} else {
	    Object[] array = (Object[])obj;
	    length = array.length;
	    writeInt(length);
	    for (i = 0; i < length; i++) {
		writeObject(array[i]);
	    }
	}
    }

    /*
     * Put the object into the stream The newObject code is written
     * followed by the ObjectStreamClass for the object's class.  Each
     * of the objects classes is written using the default
     * serialization code and dispatching to Specials where
     * appropriate.
     */
    private void outputObject(Object obj)
	throws IOException
    {
	currentObject = obj;
	Class currclass = obj.getClass();

	/* Get the Class descriptor for this class,
	 * Throw a NotSerializableException if there is none.
	 */
	currentClassDesc = ObjectStreamClass.lookup(currclass);
	if (currentClassDesc == null) {
	    throw new NotSerializableException(currclass.getName());
	}

	/* Write the code to expect an instance and
	 * the class descriptor of the instance
	 */
	writeCode(TC_OBJECT);
	outputClassDescriptor(currentClassDesc);

	/* Assign the next wirehandle */
	assignWireOffset(obj);

	/* If the object is externalizable,
	 * call writeExternal.
	 * else do Serializable processing.
	 */
	if (currentClassDesc.isExternalizable()) {
	    Externalizable ext = (Externalizable)obj;
	    ext.writeExternal(this);
	} else {

	    /* The object's classes should be processed from supertype to subtype
	     * Push all the clases of the current object onto a stack.
	     * Remember the stack pointer where this set of classes is being pushed.
	     */
	    int stackMark = classDescStack.size();
	    try {
		ObjectStreamClass next;
		while ((next = currentClassDesc.getSuperclass()) != null) {
		    classDescStack.push(currentClassDesc);
		    currentClassDesc = next;
		}

		/*
		 * For currentClassDesc and all the pushed class descriptors
		 *    If the class is writing its own data
		 *		  set blockData = true; call the class writeObject method
		 *    If not
		 *     invoke either the defaultWriteObject method.
		 */
		do {
		    if (currentClassDesc.hasWriteObject()) {
			setBlockData(true); /* Block any data the class writes */
			invokeObjectWriter(obj, currentClassDesc.forClass());
			setBlockData(false);
			writeCode(TC_ENDBLOCKDATA);
		    } else {
			defaultWriteObject();
		    }
		} while (classDescStack.size() > stackMark &&
			 (currentClassDesc = (ObjectStreamClass)classDescStack.pop()) != null);
	    } finally {
		classDescStack.setSize(stackMark);
	    }
	}
    }

    /* Serialize the reference if it is NULL or is for an object that
     * was already replaced or already serialized.
     * If the object was already replaced, look for the replacement
     * object in the known objects and if found, write its handle
     * Return True if the reference is either null or a repeat.
     */
    private boolean serializeNullAndRepeat(Object obj)
	throws IOException
    {
    	if (obj == null) {
	    writeCode(TC_NULL);
	    return true;
	}
	/* Look to see if this object has already been replaced.
	 * If so, proceed using the replacement object.
	 */
	if (replaceObjects != null) {
	    for (int i = 0; i < nextReplaceOffset; i+= 2) {
		if (replaceObjects[i] == obj) {
		    obj = replaceObjects[i+1];
		    break;
		}
	    }
	}

	int handle = findWireOffset(obj);
	if (handle >= 0) {
	    /* Add a reference to the stream */
	    writeCode(TC_REFERENCE);
	    writeInt(handle + baseWireHandle);
	    return true;
	}
	return false;		// not serialized, its up to the caller
    }

    /*
     * Locate and return if found the handle for the specified object.
     * -1 is returned if the object does not occur in the array of
     * known objects.
     */
    private int findWireOffset(Object obj) {
	int hash = System.identityHashCode(obj);
	int index = (hash & 0x7FFFFFFF) % wireHash2Handle.length;

	for (int handle = wireHash2Handle[index];
	     handle >= 0;
	     handle = wireNextHandle[handle]) {

	    if (wireHandle2Object[handle] == obj)
		return handle;
	}
	return -1;
    }

    /* Allocate a handle for an object.
     * The Vector is indexed by the wireHandleOffset
     * and contains the object.
     */
    private void assignWireOffset(Object obj)
	throws IOException
    {
	// Extend the array if there isn't room for this new element
	if (nextWireOffset == wireHandle2Object.length) {
	    Object[] oldhandles = wireHandle2Object;
	    wireHandle2Object = new Object[nextWireOffset*2];
	    System.arraycopy(oldhandles, 0,
			     wireHandle2Object, 0,
			     nextWireOffset);
	    int[] oldnexthandles = wireNextHandle;
	    wireNextHandle = new int[nextWireOffset*2];
	    System.arraycopy(oldnexthandles, 0,
			     wireNextHandle, 0,
			     nextWireOffset);
	    // TBD: Rehash the hash array if necessary
	}
	wireHandle2Object[nextWireOffset] = obj;

	hashInsert(obj, nextWireOffset);

	nextWireOffset++;
	return;
    }


    /*
     * Insert the specified object into the hash array and link if
     * necessary. Put the new object into the hash table and link the
     * previous to it. Newer objects occur earlier in the list.
     */
    private void hashInsert(Object obj, int offset) {
	int hash = System.identityHashCode(obj);
	int index = (hash & 0x7FFFFFFF) % wireHash2Handle.length;
	wireNextHandle[offset] = wireHash2Handle[index];
	wireHash2Handle[index] = offset;
    }

    /*
     * Add a replacement object to the table.
     * The even numbered indices are the original objects.
     * The odd numbered indices are the replacement objects.
     *
     */
    private void addReplacement(Object orig, Object replacement) {
	// Extend the array if there isn't room for this new element

	if (replaceObjects == null) {
	    replaceObjects = new Object[10];
	}
	if (nextReplaceOffset == replaceObjects.length) {
	    Object[] oldhandles = replaceObjects;

⌨️ 快捷键说明

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