`

java反序列化的内部实现(二)

阅读更多

 

Java 反序列化

 

Java的反序列化,是序列化的逆向过程。基础类型的序列化比较简单,这里主要讲解对象的反序列化,从文件或网络中把字节序列读取出来,并解析字节序列中的对象类描述信息、以及对象成员的值信息,并生成一个新对象的过程。

 

先看下对象反序列化的参数:字节序列(字节数组)

这里的字节序列一般是通过上一篇java序列化的内部实现(一)》中序列化过程产生的;

当然如果有必要的话也可以手工拼装(这种场景比较少);

更多是对序列化参数的字节序列进行篡改,操控反序列端的对象生成(如:被攻击者攻击)。

 

一个简单的例子

 

Java的反序列化时通过实例化ObjectInputStream,并调用其readObject方法实现的。为了方便理解,这里的字节序列采用字节数组,方便后续手动篡改。

 

package com.sky.serial;
 
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
 
/**
 * Created by gantianxing on 2017/5/29.
 */
public class UserDemo {
   
private static final byte[] serialByteArray = new byte[]{
 (byte)0xAC,(byte)0xED,0x00,0x05,0x73,0x72,0x00,0x13,0x63,0x6F,0x6D,0x2E,0x73,0x6B,0x79,0x2E,0x73,0x65,0x72,0x69,0x61,0x6C,0x2E,0x55,0x73,0x65,0x72,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x00,0x03,0x49,0x00,0x03,0x73,0x65,0x78,0x4C,0x00,0x04,0x6E,0x61,0x6D,0x65,0x74,0x00,0x12,0x4C,0x6A,0x61,0x76,0x61,0x2F,0x6C,0x61,0x6E,0x67,0x2F,0x53,0x74,0x72,0x69,0x6E,0x67,0x3B,0x4C,0x00,0x08,0x70,0x68,0x6F,0x6E,0x65,0x4E,0x75,0x6D,0x71,0x00,0x7E,0x00,0x01,0x78,0x70,0x00,0x00,0x00,0x01,0x74,0x00,0x09,0x7A,0x68,0x61,0x6E,0x67,0x20,0x73,0x61,0x6E,0x74,0x00,0x0B,0x31,0x33,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38
    };
 
 
    public static void main(String[] args) throws Exception{
 
        //拼装字节序列
        InputStream is = new ByteArrayInputStream(serialByteArray);
        ObjectInputStream in = new ObjectInputStream(is);
 
        //反序列化
        Object newObj = in.readObject();
        System.out.println(newObj.getClass());
       
        //判断是否为User类型
        if(newObj instanceof User){
            User newUser = (User)newObj;
            System.out.println("user name:" + newUser.getName());
            System.out.println(newUser);
        }
    }
}

 

 

 

执行main方法,执行结果为:

 

class com.sky.serial.User
user name:zhang san
user info: name=zhang san,sex=1,phoneNum=13888888888

 

 

 

第一行打印信息 可以看到本次反序列化生成的对象,其实就是上一篇分享示例中的User类。

第二 三行打印信息,跟上一篇分享示例中打印内容相同,细心的朋友其实已经发现这里的字节数组,就是上一篇分享示例中序列化到文件user.txt中的内容:



  

 

篡改字节序列

 

这里我们模拟把“张三”的性别sex该为女性0,即在字节数组中把0x00,0x,00,0x00,0x01改为0x00,0x,00,0x00,0x00,代码如下:

 

package com.sky.serial;
 
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
 
/**
 * Created by gantianxing on 2017/5/29.
 */
public class UserDemo {
 
    private static final byte[] serialByteArray = new byte[]{
            (byte)0xAC,(byte)0xED,0x00,0x05,0x73,0x72,0x00,0x13,0x63,0x6F,0x6D,0x2E,0x73,0x6B,0x79,0x2E
            ,0x73,0x65,0x72,0x69,0x61,0x6C,0x2E,0x55,0x73,0x65,0x72,0x00,0x00,0x00,0x00,0x00
            ,0x00,0x00,0x01,0x02,0x00,0x03,0x49,0x00,0x03,0x73,0x65,0x78,0x4C,0x00,0x04,0x6E
            ,0x61,0x6D,0x65,0x74,0x00,0x12,0x4C,0x6A,0x61,0x76,0x61,0x2F,0x6C,0x61,0x6E,0x67
            ,0x2F,0x53,0x74,0x72,0x69,0x6E,0x67,0x3B,0x4C,0x00,0x08,0x70,0x68,0x6F,0x6E,0x65
            ,0x4E,0x75,0x6D,0x71,0x00,0x7E,0x00,0x01,0x78,0x70,0x00,0x00,0x00,0x00,0x74,0x00
            ,0x09,0x7A,0x68,0x61,0x6E,0x67,0x20,0x73,0x61,0x6E,0x74,0x00,0x0B,0x31,0x33,0x38
            ,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38
    };
 
 
    public static void main(String[] args) throws Exception{
 
        //拼装字节序列
        InputStream is = new ByteArrayInputStream(serialByteArray);
        ObjectInputStream in = new ObjectInputStream(is);
 
        //反序列化
        Object newObj = in.readObject();
        System.out.println(newObj.getClass());
 
        //判断是否为User类型
        if(newObj instanceof User){
            User newUser = (User)newObj;
            System.out.println("user name:" + newUser.getName());
            System.out.println(newUser);
        }
    }
}

 

 

执行main方法,打印信息如下:

 

class com.sky.serial.User
user name:zhang san
user info: name=zhang san,sex=0,phoneNum=13888888888

 

 

 

从第三行打印信息看出,张三的性别已经变为“女性”了。

 

我们从User类的定义中看到 sexfinal的(private final int sex;),即在第一次被构造方法初始化后,就不允许被改变。但反序列化会打破这个逻辑,如何防范这种篡改呢,我将在下一遍《java序列化用法以及理论》中进行讲解。本篇重点关注,java反序列化的内容实现。

 

Java反序列化内部实现

 

1Java的反序列化时通过实例化ObjectInputStream,并调用其readObject方法实现的。我们以ObjectInputStream的构造方法为起点进行源码解析:

    

public ObjectInputStream(InputStream in) throws IOException {
        verifySubclass(); //检查是否是ObjectInputStream的子类,这里不是
        bin = new BlockDataInputStream(in);//初始化输入流对象bin,后续通过bin.readxxx()方法读取字节信息
        handles = new HandleTable(10);//已读取的
        vlist = new ValidationList();
        enableOverride = false;
        readStreamHeader();//读取魔法数+版本号(AC ED),并进行检查
        bin.setBlockDataMode(true);
}

 

 

 

//检查 魔法数+版本号方法
protected void readStreamHeader()
        throws IOException, StreamCorruptedException
    {
        short s0 = bin.readShort(); //读取STREAM_MAGIC
        short s1 = bin.readShort();//读取 STREAM_VERSION
        if (s0 != STREAM_MAGIC || s1 != STREAM_VERSION) { //如果不匹配AC ED,直接抛出异常,反序列化失败
            throw new StreamCorruptedException(
                String.format("invalid stream header: %04X%04X", s0, s1));
        }
    }

 

 

 

构造方法,主要初始化工作包括:初始化输入流对象bin,后续通过bin.readxxx()方法读取字节信息;初始化handlesvlist;读取魔法数+版本号(AC ED),并进行检查,如果检查失败直接抛出异常,反序列化失败。

 

2、调用readObjet方法(Object newObj = in.readObject();)开始反序列化:

 

public final Object readObject()
        throws IOException, ClassNotFoundException
    {
        if (enableOverride) { //判断是否开启替换,这里没有
            return readObjectOverride();
        }
 
        // if nested read, passHandle contains handle of enclosing object
        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.doCallbacks();
            }
            return obj;
        } finally {
            passHandle = outerHandle;
            if (closed && depth == 0) {
                clear();
            }
        }
}

 

 

 

readObject0方法的主要工作流程:读取TC标记,判断TC标记类型,根据不同的类型,进行不同逻辑处理。

 

 

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) { //peek读取下一个字节,这个字节是个TC标记,并判断是不是TC_RESET
            bin.readByte();
            handleReset();
        }
 
        depth++; //该方法会被递归调用,记录入栈次数
        try {
            switch (tc) { //判断TC标记的类型
                case TC_NULL:空对象标记
                    return readNull();//读取空对象
 
                case TC_REFERENCE://引用已存在的TC标记
                    return readHandle(unshared); //读取引用对象
 
                case TC_CLASS: //class类型TC标记
                    return readClass(unshared);
 
                case TC_CLASSDESC: //普通类描述TC标记
                case TC_PROXYCLASSDESC: //代理类描述TC标记
                    return readClassDesc(unshared); //
                case TC_STRING: //string TC标记
                case TC_LONGSTRING: //长string TC标记
                    return checkResolve(readString(unshared)); 本例中第二次进入readObject0方法,为对象类型成员变量name字段赋值。
 
 
                case TC_ARRAY: //数组TC标记
                    return checkResolve(readArray(unshared));
 
                case TC_ENUM://枚举TC标记
                    return checkResolve(readEnum(unshared));
 
                case TC_OBJECT://新对象TC标记73,本实例第一次进入readObject0方法,会匹配到该处
                    return checkResolve(readOrdinaryObject(unshared));
 
                case TC_EXCEPTION://异常TC标记
                    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://类描述结束TC标记
                    if (oldMode) {
                        throw new OptionalDataException(true);
                    } else {
                        throw new StreamCorruptedException(
                            "unexpected end of block data");
                    }
 
                default://没有找到匹配的 TC类型
                    throw new StreamCorruptedException(
                        String.format("invalid type code: %02X", tc));
            }
        } finally {
            depth--;
            bin.setBlockDataMode(oldMode);
        }
    }

 

 

阅读这个方法的关键是搞清楚,bin.peekByte()bin.readByte()区别,前者字节数组的index指针不会往后移,下次读取还是从原来的位置开始;后者数组的index会往后移一个字节,下次读取会从这个新位置开始。

本实例中第一次进入readObject0方法会匹配到 case TC_OBJECT: (新对象标记73),第二次进入readObject0方法会匹配到case TC_STRING:为name字段赋值,第三进入是为phoneNum字段赋值。

 

3、先看下第一次进入readObject0时,会先调用readOrdinaryObject方法,然后调用checkResolve方法checkResolve方法放到最后解释):

 

private Object readOrdinaryObject(boolean unshared)
        throws IOException
    {
        if (bin.readByte() != TC_OBJECT) {//读取出TC标记,并验证是否是新对象标记
            throw new InternalError();
        }
 
        ObjectStreamClass desc = readClassDesc(false);//根据对象的格式,下一步是读取类描述信息
        desc.checkDeserialize();
 
        Class<?> cl = desc.forClass(); //读取Class:com.sky.serial.User
        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; //通过类描述信息,初始化对象obj,注意不是通过调用构造方法初始化
        } catch (Exception ex) {
            throw (IOException) new InvalidClassException(
                desc.forClass().getName(),
                "unable to create instance").initCause(ex);
        }
 
        passHandle = handles.assign(unshared ? unsharedMarker : obj);//把对象obj放入handles map中,以便后续直接引用。
        ClassNotFoundException resolveEx = desc.getResolveException();
        if (resolveEx != null) {
            handles.markException(passHandle, resolveEx);
        }
 
        if (desc.isExternalizable()) {//判断是否实现Externalizable接口
            readExternalData((Externalizable) obj, desc); //最终调用对象的实现的readExternal方法,反序列化,为obj成员变量赋值
        } else {
            readSerialData(obj, desc);//调用默认方法为obj成员变量赋值
        }
 
        handles.finish(passHandle);
 
        if (obj != null &&
            handles.lookupException(passHandle) == null &&
            desc.hasReadResolveMethod()) //判断对象是否实现readResolve方法
        {
            Object rep = desc.invokeReadResolve(obj);//反射调用对象的readResolve方法
            if (unshared && rep.getClass().isArray()) {
                rep = cloneArray(rep);
            }
            if (rep != obj) {//如果对象readResolve返回的对象与 默认序列化对象不等,返回readResolve方法返回的对象
                handles.setObject(passHandle, obj = rep);
            }
        }
 
        return obj;
    }
 
   

 

 

  该方法会先调用readClassDesc获取对象的类描述信息,然后调用newInstance方法实例化对象obj,注意此处会生成一个空Obj(User类型)对象(成员变量值为空或者默认初始值),不是通过调用构造方法,而是JVM底层实现。然后调用readSerialData方法为空对象的成员变量赋值。

 

   newInstance方法

 

public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, null, modifiers);
            }
        }
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();
        }
        @SuppressWarnings("unchecked")
        T inst = (T) ca.newInstance(initargs);//此处会生成一个User空对象,不是通过调用构造方法,而是JVM底层实现。
        return inst;
    }

 

 

 4、调用readClassDesc方法,根据本例中的TC信息会调用readNonProxyDesc方法获取对象的类描述信息:

 

 

private ObjectStreamClass readClassDesc(boolean unshared)
        throws IOException
    {
        byte tc = bin.peekByte(); //peek读取TC标记
        switch (tc) {
            case TC_NULL:
                return (ObjectStreamClass) readNull();
 
            case TC_REFERENCE:
                return (ObjectStreamClass) readHandle(unshared);
 
            case TC_PROXYCLASSDESC:
                return readProxyDesc(unshared);
 
            case TC_CLASSDESC:
                return readNonProxyDesc(unshared); //本实例中会首先匹配到这里,开始读取类描述信息
 
            default:
                throw new StreamCorruptedException(
                    String.format("invalid type code: %02X", tc));
        }
}
 
private ObjectStreamClass readNonProxyDesc(boolean unshared)
        throws IOException
    {
        if (bin.readByte() != TC_CLASSDESC) {//读取TC标记,并验证是否是类描述TC标记
            throw new InternalError();
        }
 
        ObjectStreamClass desc = new ObjectStreamClass();
        int descHandle = handles.assign(unshared ? unsharedMarker : desc);//放入Handles map中,以便,后续遇到相同类型直接引用。
        passHandle = NULL_HANDLE;
 
        ObjectStreamClass readDesc = null;
        try {
            readDesc = readClassDescriptor(); //开始读取类描述
        } catch (ClassNotFoundException ex) {
            throw (IOException) new InvalidClassException(
                "failed to read class descriptor").initCause(ex);
        }
 
        Class<?> cl = null;
        ClassNotFoundException resolveEx = null;
        bin.setBlockDataMode(true);
        final boolean checksRequired = isCustomSubclass();//检查是否是ObjectInputStream的自定义子类,通过classLoader判断,自定义的classLoader为 App
        try {
            if ((cl = resolveClass(readDesc)) == null) {//通过反射,反射出对象类型
                resolveEx = new ClassNotFoundException("null class");
            } else if (checksRequired) {
                ReflectUtil.checkPackageAccess(cl);
            }
        } catch (ClassNotFoundException ex) {
            resolveEx = ex;
        }
        skipCustomData(); //跳过类描述结束符
 
        desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));
 
        handles.finish(descHandle);
        passHandle = descHandle;
        return desc;//返回类描述信息
    }
private void skipCustomData() throws IOException {
        int oldHandle = passHandle;
        for (;;) {
            if (bin.getBlockDataMode()) {
                bin.skipBlockData();
                bin.setBlockDataMode(false);
            }
            switch (bin.peekByte()) {
                case TC_BLOCKDATA:
                case TC_BLOCKDATALONG:
                    bin.setBlockDataMode(true);
                    break;
 
                case TC_ENDBLOCKDATA: //本实例会匹配到这里:78, 类描述读取结束
                    bin.readByte();
                    passHandle = oldHandle;
                    return;
 
                default:
                    readObject0(false);
                    break;
            }
        }
    }

 

 

5、真正读取类描述信息的方法是readClassDescriptor,所有的类信息、成员描述信息都会在本方法中完成,最终返回一个类描述信息对象ObjectStreamClass 

 

 

protected ObjectStreamClass readClassDescriptor()
        throws IOException, ClassNotFoundException
    {
        ObjectStreamClass desc = new ObjectStreamClass();
        desc.readNonProxy(this);
        return desc;
}
 
调用ObjectStreamClass类的readNonProxy方法进行类描述信息读取:
 
void readNonProxy(ObjectInputStream in)
        throws IOException, ClassNotFoundException
    {
        name = in.readUTF(); //读取类名,这里为“com.sky.serial.User”
        suid = Long.valueOf(in.readLong());//读取SUID,这里为1L
        isProxy = false;
 
        byte flags = in.readByte();//读取下一个字节,这里为02,表示该类实现了serializable接口
        hasWriteObjectData =
            ((flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0);
        hasBlockExternalData =
            ((flags & ObjectStreamConstants.SC_BLOCK_DATA) != 0);
        externalizable =
            ((flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0);
        boolean sflag =
            ((flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0);
        if (externalizable && sflag) {
            throw new InvalidClassException(
                name, "serializable and externalizable flags conflict");
        }
        serializable = externalizable || sflag; // 这里serializable赋值为true
        isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0);// 这里isEnum 本次赋值为false,User类不是范型
        if (isEnum && suid.longValue() != 0L) {//说明范型不能有非0的suid
            throw new InvalidClassException(name,
                "enum descriptor has non-zero serialVersionUID: " + suid);
        }
 
        int numFields = in.readShort(); //读取成员变量个数
        if (isEnum && numFields != 0) {
            throw new InvalidClassException(name,
                "enum descriptor has non-zero field count: " + numFields);
        }
        fields = (numFields > 0) ?
            new ObjectStreamField[numFields] : NO_FIELDS; //实例化成员变量数组
        for (int i = 0; i < numFields; i++) {//解析成员变量描述信息并放入成员变量数组
            char tcode = (char) in.readByte(); //读取成员变量type,这里的三个成员类型分别为:I(int),L(String),L(String)
            String fname = in.readUTF();//读取成员变量名称这里分别为:sex、name、phoneNum
            String signature = ((tcode == 'L') || (tcode == '[')) ?
                in.readTypeString() : new String(new char[] { tcode });//如果成员变量是对象(L)或者数组([), 还需通过in.readTypeString()获取真实类型
            try {
                fields[i] = new ObjectStreamField(fname, signature, false);//调用ObjectStreamField构造方法生成成员变量描述对象
            } catch (RuntimeException e) {
                throw (IOException) new InvalidClassException(name,
                    "invalid descriptor for field " + fname).initCause(e);
            }
        }
        computeFieldOffsets();//对primDataSize、numObjFields赋值
    }
 
ObjectStreamField构造方法:
 
ObjectStreamField(String name, String signature, boolean unshared) {
        if (name == null) {
            throw new NullPointerException();
        }
        this.name = name;
        this.signature = signature.intern();
        this.unshared = unshared;
        field = null;
 
        switch (signature.charAt(0)) {
            case 'Z': type = Boolean.TYPE; break;
            case 'B': type = Byte.TYPE; break;
            case 'C': type = Character.TYPE; break;
            case 'S': type = Short.TYPE; break;
            case 'I': type = Integer.TYPE; break;
            case 'J': type = Long.TYPE; break;
            case 'F': type = Float.TYPE; break;
            case 'D': type = Double.TYPE; break;
            case 'L':
            case '[': type = Object.class; break;
            default: throw new IllegalArgumentException("illegal signature");
        }
    }

 

 

 

该类的所有成员描述信息解析结束。回到readNonProxyDesc方法,继续判断是否有父类,如果有继续解析父类描述信息,否则开始解析成员变量的值信息,为每个成员赋值。

 

6、在readOrdinaryObject方法中根据类描述信息初始化对象obj成功后,开始为成员变量赋值,这里调用的readSerialData方法:

 

private void readSerialData(Object obj, ObjectStreamClass desc)
        throws IOException
    {
        ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();//这步获取对象父子关系拓扑结构,这里User没有直接父类(Object除外),只需要对User对象进行赋值。
        for (int i = 0; i < slots.length; i++) {//遍历拓扑结构,这里slots只有一个User对象。
            ObjectStreamClass slotDesc = slots[i].desc;//User对象的类描述信息
 
            if (slots[i].hasData) {
                if (obj == null || handles.lookupException(passHandle) != null) {
                    defaultReadFields(null, slotDesc); // skip field values
                } else if (slotDesc.hasReadObjectMethod()) {//判断是否有readObject方法,如果有通过反射调用对象的readObject方法为成员变量赋值
                    SerialCallbackContext oldContext = curContext;
                    if (oldContext != null)
                        oldContext.check();
                    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();
                        if (oldContext!= null)
                            oldContext.check();
                        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);//调用对象的readObjectNoData方法进行赋值
                }
            }
        }
    }

 

 

 

为对象成员变量赋值的默认方法defaultReadFields

 

 

private void defaultReadFields(Object obj, ObjectStreamClass desc)
        throws IOException
    {
        Class<?> cl = desc.forClass();
        if (cl != null && obj != null && !cl.isInstance(obj)) {
            throw new ClassCastException();
        }
 
        int primDataSize = desc.getPrimDataSize();
        if (primVals == null || primVals.length < primDataSize) {
            primVals = new byte[primDataSize];
        }
        bin.readFully(primVals, 0, primDataSize, false);
        if (obj != null) {
            desc.setPrimFieldValues(obj, primVals);//为prim类型赋值(如,int char等基础类型)
        }
 
        int objHandle = passHandle;
        ObjectStreamField[] fields = desc.getFields(false);
        Object[] objVals = new Object[desc.getNumObjFields()];
        int numPrimFields = fields.length - objVals.length;
        for (int i = 0; i < objVals.length; i++) {//获取对象类型成员变量值数组(如,String、数组、自定义对象等)
            ObjectStreamField f = fields[numPrimFields + i];
            objVals[i] = readObject0(f.isUnshared()); //递归调用readObject0为对象赋值,这里为String,见readObject0方法注释
            if (f.getField() != null) {
                handles.markDependency(objHandle, passHandle);
            }
        }
        if (obj != null) {
            desc.setObjFieldValues(obj, objVals); //为对象成员变量赋值
        }
        passHandle = objHandle;
    }

 

 

 

7、为基础类型赋值方法setPrimFieldValues,这里只有一个基础类型int型的sex成员变量:

 

 

void setPrimFieldValues(Object obj, byte[] buf) {
            if (obj == null) {
                throw new NullPointerException();
            }
            for (int i = 0; i < numPrimFields; i++) {
                long key = writeKeys[i];
                if (key == Unsafe.INVALID_FIELD_OFFSET) {
                    continue;           // discard value
                }
                int off = offsets[i];
                switch (typeCodes[i]) {
                    case 'Z':
                        unsafe.putBoolean(obj, key, Bits.getBoolean(buf, off));
                        break;
 
                    case 'B':
                        unsafe.putByte(obj, key, buf[off]);
                        break;
 
                    case 'C':
                        unsafe.putChar(obj, key, Bits.getChar(buf, off));
                        break;
 
                    case 'S':
                        unsafe.putShort(obj, key, Bits.getShort(buf, off));
                        break;
 
                    case 'I':
                        unsafe.putInt(obj, key, Bits.getInt(buf, off));//从字节数组buf中读取数据,为对象的sex成员变量赋值,并移动字节数组下标,类似in.read()方法。注意不是调用setxxx方法赋值,是有jvm底层实现,看不到源码。
                        break;
 
                    case 'F':
                        unsafe.putFloat(obj, key, Bits.getFloat(buf, off));
                        break;
 
                    case 'J':
                        unsafe.putLong(obj, key, Bits.getLong(buf, off));
                        break;
 
                    case 'D':
                        unsafe.putDouble(obj, key, Bits.getDouble(buf, off));
                        break;
 
                    default:
                        throw new InternalError();
                }
            }
        }

 

 

 

8、为对象类型成员变量赋值:递归调用readObject0,匹配到TC_STRING, 调用checkResolve(readString(unshared));name、phoneNum字段赋值。

 

 

private String readString(boolean unshared) throws IOException {
        String str;
        byte tc = bin.readByte();//读取TC标记
        switch (tc) {
            case TC_STRING:
                str = bin.readUTF(); //匹配到这里,读取String内容,返回                break;
 
            case TC_LONGSTRING:
                str = bin.readLongUTF();
                break;
 
            default:
                throw new StreamCorruptedException(
                    String.format("invalid type code: %02X", tc));
        }
        passHandle = handles.assign(unshared ? unsharedMarker : str); //放入handles map,
        handles.finish(passHandle);
        return str;
    }
 
为对象类型的成员变量赋值,注意不是调用的setxx方法赋值,而是jvm底层实现赋值。
void setObjFieldValues(Object obj, Object[] vals) {
            if (obj == null) {
                throw new NullPointerException();
            }
            for (int i = numPrimFields; i < fields.length; i++) {
                long key = writeKeys[i];
                if (key == Unsafe.INVALID_FIELD_OFFSET) {
                    continue;           // discard value
                }
                switch (typeCodes[i]) {
                    case 'L':
                    case '[':
                        Object val = vals[offsets[i]];
                        if (val != null &&
                            !types[i - numPrimFields].isInstance(val))
                        {
                            Field f = fields[i].getField();
                            throw new ClassCastException(
                                "cannot assign instance of " +
                                val.getClass().getName() + " to field " +
                                f.getDeclaringClass().getName() + "." +
                                f.getName() + " of type " +
                                f.getType().getName() + " in instance of " +
                                obj.getClass().getName());
                        }
                        unsafe.putObject(obj, key, val);//调用jvm底层实现为对象类型成员变量赋值
                        break;
 
                    default:
                        throw new InternalError();
                }
            }
        }

 

 

 

 

9、checkResolve方法:最后看下checkResolve方法,在使用自定义并继承ObjectInputStream类的对象进行反序列化时,可以开启enableResolve=true,并重写resolveObject方法,并用resolveObject方法返回的对象替换默认序列化对象返回:

 

private Object checkResolve(Object obj) throws IOException {
        if (!enableResolve || handles.lookupException(passHandle) != null) {
            return obj;//本示例中 到这步返回
        }
        Object rep = resolveObject(obj);//调用子类实现的resolveObject方法
        if (rep != obj) {//如果与序列化的对象不相等,则进行替换
            handles.setObject(passHandle, rep);
        }
        return rep;
    }

 

 

 

到这里整个反序列化流程结束。

 

简单总结下,对象的反序列化过程跟序列化过程类似,分两步完成:

   第一步读取类描述信息,并根据这些信息通过jvm底层实例化一个指定类型的空对象;

   第二步读取成员变量值信息对这个空对象的成员变量进行赋值,成员变量分为基础类型、对象类型,基础类型直接赋值,对象类型继续递归调用readObject0方法进行赋值,这里的赋值并不是调用的setter方法,而是调用jvm底层实现的方法进行赋值。

 

   可见对象的反序列化过程,可以不调用对象的构造方法,以及setter方法就可以完成新对象的创建,并对成员变量进行赋值。

 

其他序列化和反序列化方式

 

  本篇对象反序列化和前一篇对象序列化的示例中,都是采用默认实现Serializable接口的方式,调用默认的序列化和反序列化方法进行分析。源码中我们可以看到还有很多其他方法不在实例中体现,比如:自定义序列化方法readObjectwriteObject;实现Externalizable序列化方式的readExternalwriteExternalreadResolve替换方法;readObjectNoDataMethod方法;子类扩展的替换方法resolveObjectreplaceObject方法;以及Enum枚举类型序列化。这些方法源码处理逻辑在以上注释中都有体现,下一篇主要讲解这些方法的区别和适用场景。

 

   对象的序列化和发序列化讲解完毕,基础类型的序列化和反序列化比较简单,直接调用out.writexxx完成序列化,调用in.readxxx完成反序列化(比如out.writeInt()in.readInt())。其实对象的序列化最终也是转化为对基础类型的序列化,但是为了标记他们在对象里的关系,添加了很多TC标记而已。

1
0
分享到:
评论

相关推荐

    java面试题进阶版附答案.docx

    六、序列化和反序列化:解释了Java中序列化和反序列化的概念,以及通过实现Serializable接口进行对象的序列化和反序列化的过程。 七、内部类和匿名类:介绍了Java中的内部类和匿名类的概念,包括不同类型的内部类...

    【Java面试+Java学习指南】 一份涵盖大部分Java程序员所需要掌握的核心知识

    序列化和反序列化 继承、封装、多态的实现原理 容器 Java集合类总结 Java集合详解1:一文读懂ArrayList,Vector与Stack使用方法和实现原理 Java集合详解2:Queue和LinkedList Java集合详解3:Iterator,fail-fast机制...

    Java工程师面试复习指南

    序列化和反序列化 继承封装多态的实现原理 集合类 Java集合类总结 Java集合详解:一文读懂ArrayList,Vector与Stack使用方法和实现原理 Java集合详解:Queue和LinkedList Java集合详解:迭代器,快速失败机制与比较器...

    JAVA上百实例源码以及开源项目

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    java jdk实列宝典 光盘源代码

    序列化和反序列化对象(将不长用的对象暂时持久化到文件中为对象的序列化,反之用时再把对象恢复到内存为反序列化); 控制对象的序列化和反序列化; 读jar包的资源文件;用zip格式压缩 和解压文件;操作Excel文件;...

    JAVA基础课程讲义

    JAVA对象的序列化和反序列化 161 为什么需要序列化和反序列化 161 对象的序列化主要有两种用途 161 序列化涉及的类和接口 162 序列化/反序列化的步骤和实例 162 综合的序列化和反序列化练习 163 JAVA.IO包相关流对象...

    JAVA上百实例源码以及开源项目源代码

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    java基础案例与开发详解案例源码全

    13.9.1 序列化和反序列化操作345 13.9.2 序列化的版本347 13.1 0随机存取文件流348 13.1 1ZIP文件流350 13.1 2本章练习352 第14章 14.1 抽象窗口工具集(AWT)354 14.1.1 AWT组件和容器354 14.1.2 布局管理器359 14.2 ...

    java面试题

    65.1. java序列化、反序列化 48 65.2. 对象的序列化主要有两种用途: 48 65.3. 对象序列化包括如下步骤: 49 65.4. 对象反序列化的步骤如下: 49 66. 反射机制 49 66.1.1. 传统的类型转换。 49 66.1.2. 通过Class...

    Java2实用教程.rar

    10 10序列化与对象克隆 10 11文件锁FileLock 10 12Process类中的流 10 13带进度条的输入流 习题 第11章Java网络的基本知识 11 1使用URL 11 2读取URL中的资源 11 3显示URL资源中的HTML文件 11 4处理超链接 11 5...

    java8集合源码-Java:Java

    Java中序列化和反序列化的区别。 什么是SerialVersionUID? 内部类和子类的区别。 JSON 相对于 XML 的优势是什么? 我们可以两次导入相同的包/类吗? JVM 会在运行时加载包两次吗? 静态加载和动态类加载的区别? ...

    javascript SpiderMonkey中的函数序列化如何进行

    但其实这个操作的内部实现(引擎实现)并不是你想象的那么简单.SpiderMonkey中一共使用过两种函数序列化的技术:一种是利用反编译器(decompiler)将函数编译后的字节码反编译成源码字符串,另一种是在将函数编译成字节码...

    JAVA 范例大全 光盘 资源

    实例88 序列化和反序列化 226 实例89 Zip格式压缩、解压缩文件 228 实例90 从Jar中读取文本 232 实例91 流标记分割和统计字符串 234 实例92 Java操作Excel文件 237 第11章 Java高级特性 245 实例93 自动装箱与...

    【白雪红叶】JAVA学习技术栈梳理思维导图.xmind

    序列化 nIo 匿名类 包装类 优先级 引用 语言工具类库 容器类 集合 链表 map 工具类 系统类 日期类 数字类 字符串+正则 流 字符流 字节流 语言特性 继承 封装 多态 JVM 多线程与并发 GC...

    java范例开发大全源代码

     实例130 对象的序列化与反序列化 185  实例131 同时显示多个文件 187  实例132 生成zip压缩文件 189  实例133 解压缩zip文件 192  实例134 生成Excel文件 194  实例135 读取Excel文件中的内容 198 ...

    java范例开发大全

    实例130 对象的序列化与反序列化 185 实例131 同时显示多个文件 187 实例132 生成zip压缩文件 189 实例133 解压缩zip文件 192 实例134 生成Excel文件 194 实例135 读取Excel文件中的内容 198 实例136 生成PDF文件 ...

    Java范例开发大全 (源程序)

     实例130 对象的序列化与反序列化 185  实例131 同时显示多个文件 187  实例132 生成zip压缩文件 189  实例133 解压缩zip文件 192  实例134 生成Excel文件 194  实例135 读取Excel文件中的内容 198  ...

    java面试题,180多页,绝对良心制作,欢迎点评,涵盖各种知识点,排版优美,阅读舒心

    如何实现Java序列化与反序列化 28 【基础】String s = new String("xyz");创建了几个字符串对象 30 【基础】接口是否可继承(extends)接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concrete...

Global site tag (gtag.js) - Google Analytics