JDK 1.7 java.io 源码学习之ObjectInputStream和ObjectOutputStream

本文深入探讨了Java中ObjectInputStream和ObjectOutputStream的使用,涉及序列化和反序列化过程。讲解了DataInput、DataOutput、ObjectInput和ObjectOutput接口的作用,并分析了ObjectInputStream的readObject方法以及如何通过enableOverride实现自定义序列化。同样,文章还简要提及了ObjectOutputStream的writeObject方法的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇Serializable接口,描述了序列化和反序列化机制,其操作均是基于ObjectInputStream和ObjectOutputStream。

根据ObjectInputStream和ObjectOutputStrema类结构,需先了解两个接口:
DataInput和DataOutput
DataInput和DataOutput 用于从字节流中读取/写入Java基本类型数据或UTF编码的字符串,比较特殊的是,到达流末尾时通过抛出EOFException的方式来表示的

ObjectInput和ObjectOutput分别扩展了DataInput和DataOutput:
ObjectInput扩展了DataInput,使其支持读取对象,并缓存到一个字节数组中
ObjectOutput扩展了DataOutpu,使其可以直接将对象写入流,货主写入字节数组中

ObjectInputStream和ObjectOutputStream是比较复杂,这次主要是学习其在序列化和反序列化上的使用,以及通过readObject和writeObject实现自定义序列化的原理。

在实例化ObjectInputStream和ObjectOutputStream时,均是通过其有参构造函数实例化,传递一个FileInputStream或FileOutputStream对象。

ObjectInputStream的readObject方法:

public final Object readObject() throws IOException, ClassNotFoundException {
    if(enableOverrid) {
        return readObjectOverride();
    }
    int outerHandle = passHandle;
    try {
        Object obj = readObject0(false);
        handles.markDependency(outerHandle, passHandle);
        ClassNotFoundException ex = handles.lookupException(passHandle);
        if (ex != null) {
            throw ex;
        }
        if (depth == 0) {
            vlist.doCallback();
        }
        return obj;
    } finally {
        passHandle = outerHandle;
        if (closed && depth == 0) {
            clear();
        }
    }
}

readObject方法是final的,表示不可被Override,但是其已经通过enableOverrid标记实现了扩展,若想自定义实现,可以Override这个方法:

protected Object readObjectOverride() throws IOException, ClassNotFoundException {
    return null;
}

该方式非常精妙,从另一种角度,实现了final修饰的方法的Override!而且将真正的处理方法封装到readObject0中

readObject方法核心的就是调用了readObject0方法:

try {
    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:
        case TC_LONGSTRING:
            return checkResolve(readString(unshared));

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

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

        case TC_OBJECT:
            return checkResolve(readOrdinaryObject(unshared));

        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);
}

很明显的就是不同类型的调用不同的处理方法,这边主要看Object类型的:
checkResolve(readOrdinaryObject(unshared))

private Object readOrdinaryObject(boolean unshared)
        throws IOException
    {
        if (bin.readByte() != TC_OBJECT) {
            throw new InternalError();
        }

        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 {
            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) {
                handles.setObject(passHandle, obj = rep);
            }
        }

        return obj;
    }

里面这两句表示了不同的处理方式:

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

即实现Serializable接口的调用readSerialData(obj, desc),实现Externalizable接口的调用readExternalData((Externalizable) obj, desc),以readSerialData为例:

private void readSerialData(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 (slots[i].hasData) {
                if (obj != null &&
                    slotDesc.hasReadObjectMethod() &&
                    handles.lookupException(passHandle) == null)
                {
                    SerialCallbackContext oldContext = curContext;

                    try {
                        curContext = new SerialCallbackContext(obj, slotDesc);

                        bin.setBlockDataMode(true);
                        slotDesc.invokeReadObject(obj, this);
                    } catch (ClassNotFoundException ex) {
                        /*
                         * In most cases, the handle table has already
                         * propagated a CNFException to passHandle at this
                         * point; this mark call is included to address cases
                         * where the custom readObject method has cons'ed and
                         * thrown a new CNFException of its own.
                         */
                        handles.markException(passHandle, ex);
                    } finally {
                        curContext.setUsed();
                        curContext = oldContext;
                    }

                    /*
                     * defaultDataEnd may have been set indirectly by custom
                     * readObject() method when calling defaultReadObject() or
                     * readFields(); clear it to restore normal read behavior.
                     */
                    defaultDataEnd = false;
                } else {
                    defaultReadFields(obj, slotDesc);
                }
                if (slotDesc.hasWriteObjectData()) {
                    skipCustomData();
                } else {
                    bin.setBlockDataMode(false);
                }
            } else {
                if (obj != null &&
                    slotDesc.hasReadObjectNoDataMethod() &&
                    handles.lookupException(passHandle) == null)
                {
                    slotDesc.invokeReadObjectNoData(obj);
                }
            }
        }
    }

里面进行了判断,slotDesc.hasReadObjectMethod即类里面有如果含有ReadObjectMethod,最终是执行了slotDesc.invokeReadObject(obj, this)。而invokeReadObject即通过反射的方式调用了该方法:

 void invokeReadObject(Object obj, ObjectInputStream in)
        throws ClassNotFoundException, IOException,
               UnsupportedOperationException
    {
        if (readObjectMethod != null) {
            try {
                readObjectMethod.invoke(obj, new Object[]{ in });
            } catch (InvocationTargetException ex) {
                Throwable th = ex.getTargetException();
                if (th instanceof ClassNotFoundException) {
                    throw (ClassNotFoundException) th;
                } else if (th instanceof IOException) {
                    throw (IOException) th;
                } else {
                    throwMiscException(th);
                }
            } catch (IllegalAccessException ex) {
                // should not occur, as access checks have been suppressed
                throw new InternalError();
            }
        } else {
            throw new UnsupportedOperationException();
        }
    }

invoke方法即是通过反射机制调用,故readObject方法private也是可以的,而且用private修饰以后,还能防止子类Override

ObjectOutputStream 与ObjectInputStream类似:

public final void writeObject(Object obj) throws IOException {
        if (enableOverride) {
            writeObjectOverride(obj);
            return;
        }
        try {
            writeObject0(obj, false);
        } catch (IOException ex) {
            if (depth == 0) {
                writeFatalException(ex);
            }
            throw ex;
        }
}

writeObject也支持通过enableOverride方式扩展,被Override,也是调用的writeObject0方法,writeObject0方法里也有一个条件判断语句:

if (obj instanceof String) {
       writeString((String) obj, unshared);
} else if (cl.isArray()) {
       writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
       writeEnum((Enum) obj, desc, unshared);
} else if (obj instanceof Serializable) {
       writeOrdinaryObject(obj, desc, unshared);
} else {
       if (extendedDebugInfo) {
            throw new NotSerializableException(
                 cl.getName() + "\n" + debugInfoStack.toString());
       } else {
                 throw new NotSerializableException(cl.getName());
       }
}

也是根据类型调用不同的处理方法,以实现Serializable接口的为例:

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);
            writeClassDesc(desc, false);
            handles.assign(unshared ? null : obj);
            if (desc.isExternalizable() && !desc.isProxy()) {
                writeExternalData((Externalizable) obj);
            } else {
                writeSerialData(obj, desc);
            }
        } finally {
            if (extendedDebugInfo) {
                debugInfoStack.pop();
            }
        }
    }

也是根据Serializable和Externalizable接口的不同调用不同的方法,具体就不贴出源码了

看的比较粗糙,后面继续完善

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值