serialization

ObjectOutputStream

The core step

1. Convert objects to binary

/** * 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. * *@throws  InvalidClassException Something is wrong with a class used by
 *          serialization.
 * @throws  NotSerializableException Some object to be serialized does not
 *          implement the java.io.Serializable interface.
 * @throws  IOException Any exception thrown by the underlying
 *          OutputStream.
 */
public final void writeObject(Object obj) throws IOException {
    if (enableOverride) {
        writeObjectOverride(obj);
        return;
    }
    try {
        writeObject0(obj, false); // Call the underlying method
    } catch (IOException ex) {
        if (depth == 0) {
            writeFatalException(ex);
        }
        throwex; }}Copy the code

The core step

  1. Type of the verification object
  2. If it is a normal object, the writeOrdinaryObject method is called
/** * Underlying writeObject/writeUnshared implementation. */
private void writeObject0(Object obj, boolean unshared)
    throws IOException
{
    boolean oldMode = bout.setBlockDataMode(false);
    depth++;
    try {
        // handle previously written and non-replaceable objects
        int h;
        if ((obj = subs.lookup(obj)) == null) {
            writeNull();
            return;
        } else if(! unshared && (h = handles.lookup(obj)) ! = -1) {
            writeHandle(h);
            return;
        } else if (obj instanceof Class) {
            writeClass((Class) obj, unshared);
            return;
        } else if (obj instanceof ObjectStreamClass) {
            writeClassDesc((ObjectStreamClass) obj, unshared);
            return;
        }

        // check for replacement objectObject orig = obj; Class<? > cl = obj.getClass(); ObjectStreamClass desc;for (;;) {
            // REMIND: skip this check for strings/arrays?Class<? > repCl; desc = ObjectStreamClass.lookup(cl,true);
            if(! desc.hasWriteReplaceMethod() || (obj = desc.invokeWriteReplace(obj)) ==null ||
                (repCl = obj.getClass()) == cl)
            {
                break;
            }
            cl = repCl;
        }
        if (enableReplace) {
            Object rep = replaceObject(obj);
            if(rep ! = obj && rep ! =null) {
                cl = rep.getClass();
                desc = ObjectStreamClass.lookup(cl, true);
            }
            obj = rep;
        }

        // if object replaced, run through original checks a second time
        if(obj ! = orig) { subs.assign(orig, obj);if (obj == null) {
                writeNull();
                return;
            } else if(! unshared && (h = handles.lookup(obj)) ! = -1) {
                writeHandle(h);
                return;
            } else if (obj instanceof Class) {
                writeClass((Class) obj, unshared);
                return;
            } else if (obj instanceof ObjectStreamClass) {
                writeClassDesc((ObjectStreamClass) obj, unshared);
                return; }}// Remaining cases // Remaining cases
        // The type of the verification object
        if (obj instanceof String) { / / string
            writeString((String) obj, unshared);
        } else if (cl.isArray()) { / / array
            writeArray(obj, desc, unshared);
        } else if (obj instanceofEnum) { writeEnum((Enum<? >) obj, desc, unshared); }else if (obj instanceof Serializable) { // serializable
            // Write ordinary objects
            writeOrdinaryObject(obj, desc, unshared);
        } else {
            if (extendedDebugInfo) {
                throw new NotSerializableException(
                    cl.getName() + "\n" + debugInfoStack.toString());
            } else {
                throw newNotSerializableException(cl.getName()); }}}finally{ depth--; bout.setBlockDataMode(oldMode); }}Copy the code

The core step

  1. Write the class description
  2. Convert objects to binary
/** * Writes representation of a "ordinary" (i.e., not a String, Class, * ObjectStreamClass, array, or enum constant) serializable object to the * stream. */
private void writeOrdinaryObject(Object obj,
                                 ObjectStreamClass desc,
                                 boolean unshared)
    throws IOException
{
    if (extendedDebugInfo) {
        debugInfoStack.push(
            (depth == 1 ? "root " : "") + "object (class "" + obj.getClass().getName() + ""," + obj.toString() + ")");
    }
    try {
        desc.checkSerialize();

        bout.writeByte(TC_OBJECT);
        // Write the class description
        writeClassDesc(desc, false);
        handles.assign(unshared ? null : obj);
        if(desc.isExternalizable() && ! desc.isProxy()) { writeExternalData((Externalizable) obj); }else {
            // Convert the object to binarywriteSerialData(obj, desc); }}finally {
        if(extendedDebugInfo) { debugInfoStack.pop(); }}}Copy the code

The core step

1. Convert objects to binary

/** * Writes instance data for each serializable class of given object, from * superclass to subclass. */
private void writeSerialData(Object obj // ObjectStreamClass desc)
    throws IOException
{
    ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
    for (int i = 0; i < slots.length; i++) {
        ObjectStreamClass slotDesc = slots[i].desc;
        if (slotDesc.hasWriteObjectMethod()) {
            PutFieldImpl oldPut = curPut;
            curPut = null;
            SerialCallbackContext oldContext = curContext;

            if (extendedDebugInfo) {
                debugInfoStack.push(
                    "custom writeObject data (class "" + slotDesc.getName() + "")");
            }
            try {
                curContext = new SerialCallbackContext(obj, slotDesc);
                bout.setBlockDataMode(true);
                slotDesc.invokeWriteObject(obj, this);
                bout.setBlockDataMode(false);
                bout.writeByte(TC_ENDBLOCKDATA);
            } finally {
                curContext.setUsed();
                curContext = oldContext;
                if (extendedDebugInfo) {
                    debugInfoStack.pop();
                }
            }

            curPut = oldPut;
        } else {
            // Convert the object to binarydefaultWriteFields(obj, slotDesc); }}}Copy the code

The core step

  1. Write the basic data type first
  2. Write non-basic data types
/** * Fetches and writes values of serializable fields of given object to * stream. The given class descriptor specifies  which field values to * write, and in which order they should be written. */
private void defaultWriteFields(Object obj, ObjectStreamClass desc)
    throws IOException
{ Class<? > cl = desc.forClass();if(cl ! =null&& obj ! =null && !cl.isInstance(obj)) {
        throw new ClassCastException();
    }

    desc.checkDefaultSerialize();

    // Write the underlying data type first
    int primDataSize = desc.getPrimDataSize(); // Get the underlying data type
    if (primVals == null || primVals.length < primDataSize) {
        primVals = new byte[primDataSize];
    }
    desc.getPrimFieldValues(obj, primVals);
    bout.write(primVals, 0, primDataSize, false);

    // Write the non-base data type again
    ObjectStreamField[] fields = desc.getFields(false); // Get non-base data type fields
    Object[] objVals = new Object[desc.getNumObjFields()];
    int numPrimFields = fields.length - objVals.length;
    desc.getObjFieldValues(obj, objVals);
    for (int i = 0; i < objVals.length; i++) { // Loop through non-base data type fields
        if (extendedDebugInfo) {
            debugInfoStack.push(
                "field (class "" + desc.getName() + "", name: "" + fields[numPrimFields + i].getName() + "", type: "" + fields[numPrimFields + i].getType() + "")");
        }
        try {
            writeObject0(objVals[i],
                         fields[numPrimFields + i].isUnshared()); // Write non-basic data type fields
        } finally {
            if(extendedDebugInfo) { debugInfoStack.pop(); }}}}Copy the code

deserialization

ObjectInputStream

To read an object, the underlying call is the readObject0 method.

/**
 * Read an object from the ObjectInputStream.  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 read.
 * Default deserializing for a class can be overridden using the writeObject
 * and readObject methods.  Objects referenced by this object are read
 * transitively so that a complete equivalent graph of objects is
 * reconstructed by readObject.
 *
 * <p>The root object is completely restored when all of its fields and the
 * objects it references are completely restored.  At this point the object
 * validation callbacks are executed in order based on their registered
 * priorities. The callbacks are registered by objects (in the readObject
 * special methods) as they are individually restored.
 *
 * <p>Exceptions are thrown for problems with the InputStream and for
 * classes that should not be deserialized.  All exceptions are fatal to
 * the InputStream and leave it in an indeterminate state; it is up to the
 * caller to ignore or recover the stream state.
 *
 * @throws  ClassNotFoundException Class of a serialized object cannot be
 *          found.
 * @throws  InvalidClassException Something is wrong with a class used by
 *          serialization.
 * @throws  StreamCorruptedException Control information in the
 *          stream is inconsistent.
 * @throws  OptionalDataException Primitive data was found in the
 *          stream instead of objects.
 * @throws  IOException Any of the usual Input/Output related exceptions.
 */
public final Object readObject(a)
    throws IOException, ClassNotFoundException
{
    if (enableOverride) {
        return readObjectOverride();
    }

    // if nested read, passHandle contains handle of enclosing object
    int outerHandle = passHandle;
    try {
        Object obj = readObject0(false); // The underlying method of reading objects
        handles.markDependency(outerHandle, passHandle);
        ClassNotFoundException ex = handles.lookupException(passHandle);
        if(ex ! =null) {
            throw ex;
        }
        if (depth == 0) {
            vlist.doCallbacks();
        }
        return obj;
    } finally {
        passHandle = outerHandle;
        if (closed && depth == 0) { clear(); }}}Copy the code

The core step

  1. Type of the verification object

Is it a string? An array? Or ordinary objects?

  1. If it is a normal object, the readOrdinaryObject method is called
/** * Underlying readObject implementation. */
private Object readObject0(boolean unshared) throws IOException {
    boolean oldMode = bin.getBlockDataMode();
    if (oldMode) {
        int remain = bin.currentBlockRemaining();
        if (remain > 0) {
            throw new OptionalDataException(remain);
        } else if (defaultDataEnd) {
            /* * Fix for 4360508: stream is currently at the end of a field * value block written via default serialization; since there * is no terminating TC_ENDBLOCKDATA tag, simulate * end-of-custom-data behavior explicitly. */
            throw new OptionalDataException(true);
        }
        bin.setBlockDataMode(false);
    }

    byte tc;
    while ((tc = bin.peekByte()) == TC_RESET) {
        bin.readByte();
        handleReset();
    }

    depth++;
    totalObjectRefs++;
    try {
    // What type is the verification object
        switch (tc) { 
            case TC_NULL:
                return readNull();

            case TC_REFERENCE:
                return readHandle(unshared);

            case TC_CLASS:
                return readClass(unshared);

            case TC_CLASSDESC:
            case TC_PROXYCLASSDESC:
                return readClassDesc(unshared);

            case TC_STRING: / / string
            
            case TC_LONGSTRING:
                return checkResolve(readString(unshared));

            case TC_ARRAY: / / array
                return checkResolve(readArray(unshared));

            case TC_ENUM:
                return checkResolve(readEnum(unshared));

            case TC_OBJECT:
                return checkResolve(readOrdinaryObject(unshared)); // Read ordinary objects

            case TC_EXCEPTION:
                IOException ex = readFatalException();
                throw new WriteAbortedException("writing aborted", ex);

            case TC_BLOCKDATA:
            case TC_BLOCKDATALONG:
                if (oldMode) {
                    bin.setBlockDataMode(true);
                    bin.peek();             // force header read
                    throw new OptionalDataException(
                        bin.currentBlockRemaining());
                } else {
                    throw new StreamCorruptedException(
                        "unexpected block data");
                }

            case TC_ENDBLOCKDATA:
                if (oldMode) {
                    throw new OptionalDataException(true);
                } else {
                    throw new StreamCorruptedException(
                        "unexpected end of block data");
                }

            default:
                throw new StreamCorruptedException(
                    String.format("invalid type code: %02X", tc)); }}finally{ depth--; bin.setBlockDataMode(oldMode); }}Copy the code

The core step

  1. Read the fields of the object
  2. Create an object
/** * Reads and returns "ordinary" (i.e., not a String, Class, * ObjectStreamClass, array, or enum constant) object, or null if object's * class is unresolvable (in which case a ClassNotFoundException will be * associated with object's handle). Sets passHandle to object's assigned * handle. */
private Object readOrdinaryObject(boolean unshared)
    throws IOException
{
    if(bin.readByte() ! = TC_OBJECT) {throw new InternalError();
    }
    
// Read the fields of the object
    ObjectStreamClass desc = readClassDesc(false); desc.checkDeserialize(); Class<? > cl = desc.forClass();if (cl == String.class || cl == Class.class
            || cl == ObjectStreamClass.class) {
        throw new InvalidClassException("invalid class descriptor");
    }

    Object obj;
    try {
    // Create an object if it can be instantiated
        obj = desc.isInstantiable() ? desc.newInstance() : null; 
    } catch (Exception ex) {
        throw (IOException) new InvalidClassException(
            desc.forClass().getName(),
            "unable to create instance").initCause(ex);
    }

    passHandle = handles.assign(unshared ? unsharedMarker : obj);
    ClassNotFoundException resolveEx = desc.getResolveException();
    if(resolveEx ! =null) {
        handles.markException(passHandle, resolveEx);
    }

    if (desc.isExternalizable()) {
        readExternalData((Externalizable) obj, desc);
    } else {
        readSerialData(obj, desc);
    }

    handles.finish(passHandle);

    if(obj ! =null &&
        handles.lookupException(passHandle) == null &&
        desc.hasReadResolveMethod())
    {
        Object rep = desc.invokeReadResolve(obj);
        if (unshared && rep.getClass().isArray()) {
            rep = cloneArray(rep);
        }
        if(rep ! = obj) {// Filter the replacement object
            if(rep ! =null) {
                if (rep.getClass().isArray()) {
                    filterCheck(rep.getClass(), Array.getLength(rep));
                } else {
                    filterCheck(rep.getClass(), -1); } } handles.setObject(passHandle, obj = rep); }}return obj;
}
Copy the code

ObjectStreamClass

The core step

  1. Create an object
/** * Creates a new instance of the represented class. If the class is * externalizable, invokes its public no-arg constructor; otherwise, if the * class is serializable, invokes the no-arg constructor of the first * non-serializable superclass. Throws UnsupportedOperationException if * this class descriptor is not associated with a class, if the associated * class is non-serializable or if the appropriate no-arg constructor is * inaccessible/unavailable. */
Object newInstance(a)
    throws InstantiationException, InvocationTargetException,
           UnsupportedOperationException
{
    requireInitialized();
    if(cons ! =null) {
        try {
            if (domains == null || domains.length == 0) {
                return cons.newInstance(); // Create an object
            } else{ JavaSecurityAccess jsa = SharedSecrets.getJavaSecurityAccess(); PrivilegedAction<? > pea = () -> {try {
                        return cons.newInstance();
                    } catch (InstantiationException
                             | InvocationTargetException
                             | IllegalAccessException x) {
                        throw newUndeclaredThrowableException(x); }};// Can't use PrivilegedExceptionAction with jsa
                try {
                    return jsa.doIntersectionPrivilege(pea,
                               AccessController.getContext(),
                               new AccessControlContext(domains));
                } catch (UndeclaredThrowableException x) {
                    Throwable cause = x.getCause();
                    if (cause instanceof InstantiationException)
                        throw (InstantiationException) cause;
                    if (cause instanceof InvocationTargetException)
                        throw (InvocationTargetException) cause;
                    if (cause instanceof IllegalAccessException)
                        throw (IllegalAccessException) cause;
                    // not supposed to happen
                    throwx; }}}catch (IllegalAccessException ex) {
            // should not occur, as access checks have been suppressed
            throw newInternalError(ex); }}else {
        throw newUnsupportedOperationException(); }}Copy the code

The bottom line is to call the constructor’s newInstance method to create an object, and the bottom line is to call the native method:

NativeConstructorAccessorImpl class

private static native Object newInstance0(Constructor
        var0, Object[] var1) throws InstantiationException, IllegalArgumentException, InvocationTargetException;
Copy the code

reference

Developer.aliyun.com/article/643…

Zhzhdoai. Making. IO / 2020/02/07 /…