• java高级---->Serializable的过程分析


      本次讲解中我们在上次的基础上,深入的了解一下序列化的流程以及其中的原理。关于序列化的一些知识与使用,请参见我的另一篇博客:java基础---->Serializable的使用。好了,我们进行以下分析的讲解。

    目录导航

    1.   Java序列化的原理分析
    2.   自定义Serializable的使用
    3.   友情链接

    Java序列化的原理分析

    java基础---->Serializable的使用的代码的演示,我们可以知道:

    • 调用writeObject方法序列化一个对象,是将其写入磁盘,以后在程序再次调用readObject时,根据wirteObject方法磁盘的文件重新恢复那个对象
    • Externalizable 接口扩展了Serializable,并增添了两个方法:writeExternal()和readExternal()。在序列化和重新装配的过程中,会自动调用这两个方法

    Java的序列化

    一、 objectOutputStream.writeObject(user)方法执行的详细如下:

    • ObjectOutputStream的构造函数设置enableOverride = false;
    public ObjectOutputStream(OutputStream out) throws IOException {
        verifySubclass();
        bout = new BlockDataOutputStream(out);
        handles = new HandleTable(10, (float) 3.00);
        subs = new ReplaceTable(10, (float) 3.00);
        enableOverride = false;
        writeStreamHeader();
        bout.setBlockDataMode(true);
        if (extendedDebugInfo) {
            debugInfoStack = new DebugTraceInfoStack();
        } else {
            debugInfoStack = null;
        }
    }
    • 所以writeObject方法执行的是writeObject0(obj, false);
     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;
        }
    }

     二、 在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() + "
    " + debugInfoStack.toString());
        } else {
            throw new NotSerializableException(cl.getName());
        }
    }
    • 从上可以看出,如果对象没有实现Serializable接口,在序列化的时候会抛出NotSerializableException异常
    • 上述Person是一个类对象,所以会执行writeOrdinaryObject(obj, desc, unshared)方法;

    三、 在writeOrdinaryObject(obj, desc, unshared)方法中,重要代码如下:

    if (desc.isExternalizable() && !desc.isProxy()) {
        writeExternalData((Externalizable) obj);
    } else {
        writeSerialData(obj, desc);
    }
    • 如果对象实现了Externalizable接口,那么执行writeExternalData((Externalizable) obj)方法
    • 如果对象实现的是Serializable接口,那么执行的是writeSerialData(obj, desc);

    四, 我们首先看一下writeSerialData方法,主要执行方法:defaultWriteFields(obj, slotDesc);

    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();
    
        int primDataSize = desc.getPrimDataSize();
        if (primVals == null || primVals.length < primDataSize) {
            primVals = new byte[primDataSize];
        }
        desc.getPrimFieldValues(obj, primVals);
        bout.write(primVals, 0, primDataSize, false);
    
        ObjectStreamField[] fields = desc.getFields(false);
        Object[] objVals = new Object[desc.getNumObjFields()];
        int numPrimFields = fields.length - objVals.length;
        desc.getObjFieldValues(obj, objVals);
        for (int i = 0; i < objVals.length; i++) {
            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());
            } finally {
                if (extendedDebugInfo) {
                    debugInfoStack.pop();
                }
            }
        }
    }
    • 在此方法中,是系统默认的写入对象的非transient部分。我们在后续的自定义序列化中会做讲解。

    五、 我们再来看一下writeExternalData的方法,重要代码如下:

    try {
        curContext = null;
        if (protocol == PROTOCOL_VERSION_1) {
            obj.writeExternal(this);
        } else {
            bout.setBlockDataMode(true);
            obj.writeExternal(this);
            bout.setBlockDataMode(false);
            bout.writeByte(TC_ENDBLOCKDATA);
        }
    }
    • 在此方法中obj.writeExternal(this)执行了序列化对象的writeExternal方法,在上述例子中也就是User类的writeExternal方法。

    Java的反序列化

    一、 与上述的write过程类似,objectInputStream.readObject()方法执行了readObject0(false)方法:主要代码:

    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));
    }
    • 根据不同的对象类型做相应的处理,这里我们关注的是TC_OBJECT,执行的方法是:checkResolve(readOrdinaryObject(unshared));

    七、 跟进去,我们可以看到在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;
    }
    • 加载序列化对象的Class,如果可以实例化,那么创建它的实例:desc.newInstance(),User类的无参构造方法执行

    自定义Serializable的使用

    一、 我们写了Human类,为了方便Main方法也在这里类里面。

    package com.huhx.model;
    
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    
    public class Human implements Serializable{
    
        private static final long serialVersionUID = 1L;
        
        private String username;
        private transient String password;
        
        public Human(String username, String password) {
            this.username = "not transient " + username;
            this.password = "transient " + password;
        }
        
        @Override
        public String toString() {
            return username + "
    " + password;
        }
        
        private void readObject(ObjectInputStream inputStream) throws ClassNotFoundException, IOException {
            inputStream.defaultReadObject();
            password = (String) inputStream.readObject();
        }
        
        private void writeObject(ObjectOutputStream outputStream) throws IOException {
            outputStream.defaultWriteObject();
            outputStream.writeObject(password);
        }
        
        public static void main(String[] args) throws Exception {
            Human human = new Human("huhx", "liuli");
            System.out.println("before: 
    " + human);
            
            ByteArrayOutputStream buff = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(buff);
            objectOutputStream.writeObject(human);
            
            ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(buff.toByteArray()));
            Human human2 = (Human) objectInputStream.readObject();
            System.out.println("after: 
    " + human2);
        }
    }
    • 定义了两种方法:readObject和writeObject方法,注意命名是有限制的,这个等下解释。好了,我们先看结果

      

    • 一个String 保持原始状态,其他设为transient(临时),以便证明非临时字段会被defaultWriteObject()方法自动保存,而transient 字段必须在程序中明确保存和恢复。字段是在构建器内部初始化的,而不是在定义的时候,这证明了它们不会在重新装配的时候被某些自动化机制初始化。若准备通过默认机制写入对象的非transient 部分,那么必须调用defaultWriteObject(),令其作为writeObject()中的第一个操作;并调用defaultReadObject(),令其作为readObject()的第一个操作

    二、 Human类中的readObject和writeObject方法的执行,不是我们调用的,而是ObjectInputStream与ObjectOutputStream 对象的readObject,writeObject方法去调用的。重要源代码如下

    •  ObjectStreamClass(final Class<?> cl) 构造方法:
    if (externalizable) {
        cons = getExternalizableConstructor(cl);
    } else {
        cons = getSerializableConstructor(cl);
        writeObjectMethod = getPrivateMethod(cl, "writeObject",
            new Class<?>[] { ObjectOutputStream.class },
            Void.TYPE);
        readObjectMethod = getPrivateMethod(cl, "readObject",
            new Class<?>[] { ObjectInputStream.class },
            Void.TYPE);
        readObjectNoDataMethod = getPrivateMethod(
            cl, "readObjectNoData", null, Void.TYPE);
        hasWriteObjectData = (writeObjectMethod != null);
    }
    slotDesc.invokeWriteObject(obj, this);

    友情链接

  • 相关阅读:
    .bat文件打开指定网页,并运行jar包
    jar包制作一个可执行文件
    如何让局域网其他电脑通过IP直接访问自己电脑的网站
    Sypder 安装和使用
    tomcat服务器输入localhost可以访问,ip无法访问解决办法
    【转载】高性能网站建设
    网站优化
    JavaWeb 项目开发中的技术总结
    反射工具类——ReflectUtils
    Ajax 的缺点
  • 原文地址:https://www.cnblogs.com/huhx/p/sSerializableTheory.html
Copyright © 2020-2023  润新知