Java序列化

前言

序列化并不是Java语言独有的机制,它表示的是将一个对象的状态信息转换成可传输或可存储的数据格式的过程,当需要再次使用该对象时通过反序列化将对象还原。

例如在有些场景下我们可能需要将Java对象传输给网络另一端的JVM上使用,像调用RPC方法传递参数对象,或者有时我们希望Java对象的生命周期能比JVM长,即使JVM停止运行了下一次重启也能继续使用,这时候可以通过将Java对象序列化后保存到文件系统,下次需要使用时通过反序列化还原成之前的Java对象。

在日常开发中我们经常接触到的序列化主要有基于XML数据格式的序列化、基于JSON格式的序列化和Java原始的序列化,本文只讨论Java原生支持的序列化。

基本用法

要将Java对象序列化为二进制字节码,被序列化的对象必须实现java.io.Serializable接口或者java.io.Externalizable接口。

java.io.Serializable是一个空接口,仅起到一个标识作用,标识该对象可以被序列化,当对实现了该接口的对象进行序列化时,会使用Java默认的序列化方式进行序列化。

java.io.Externalizable接口则是Java提供的一种支持自定义序列化的方法,实现该接口必须由开发者提供void readExternal(ObjectInput in)void writeExternal(ObjectOutput out)两个方法的实现。

如果没有实现这两个接口中的其中一个,那么在序列化时会抛出java.io.NotSerializableException

通常实现Serializable接口是最简单快速支持序列化的方案,可以通过ObjectOutputStream.writeObject(Object obj)方法将参数指定的obj对象序列化为二进制字节码,并把得到的字节码写入一个目标输出流,例如FileOutputStream中。而使用ObjectInputStream.readObject()方法则可以从一个源输入流中读取字节序列,反序列化为Java对象返回。

代码示例

  • 序列化对象Person实现Serializable
package com.liang;

import java.io.Serializable;

public class Person implements Serializable {

	private static final long serialVersionUID = 1L;
	private String name;
	private int age;
	private transient String address;
	private Person parent;

	public Person(String name, int age, String address) {
		this.name = name;
		this.age = age;
		this.address = address;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public Person getParent() {
		return parent;
	}

	public void setParent(Person parent) {
		this.parent = parent;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + ", address=" + address + ", parent=" + parent + "]";
	}

}
  • 使用ObjectOutputStream/OutjectInputStream进行序列化、反序列化
package com.liang;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SerializableTest {

	private static final String FILE_NAME = "temp.out";

	public static void main(String[] args) {
		// 序列化
		Person son = new Person("xiaoming", 10, "广州");
		Person parent = new Person("daming", 40, "上海");
		son.setParent(parent);
		System.out.println("before serialization: " + son);
		try {
			ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(FILE_NAME));
			oos.writeObject(son);
			oos.flush();
			oos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

		// 反序列化
		try {
			ObjectInputStream ois = new ObjectInputStream(new FileInputStream(FILE_NAME));
			Person person = (Person) ois.readObject();
			System.out.println("after deserialization: " + person);
			ois.close();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

	}
}

程序运行结束,控制台输出:

此处输入图片的描述

用UltraEdit或者MadEdit打开temp.out,结果显示如下:

Java序列化_第1张图片

几点说明:

  • serialVersionUID常量的作用是为了Java在反序列化过程中保证对象的唯一性,如果序列化时的serialVersionUID与反序列化时使用的serialVersionUID不一致,将会抛出java.io.InvalidClassException异常。
  • 对象的成员属性在序列化过程中会被递归的序列化,所以如果成员属性是引用类型也必须实现Serializable或Externalizab接口。
  • 除serialVersionUID外,对象中的静态成员变量和transient关键字修饰的成员变量在序列化过程中不会被序列化。

源码分析

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(FILE_NAME));方法上打个断点,跟踪函数的调用过程和相关变量变化,相关源码如下:

  • 实例化ObjectOutputSObjectOutputStream.new ObjectOutputStream(OutputStream out)
public ObjectOutputStream(OutputStream out) throws IOException {
    //进行安全检测
    verifySubclass(); 
    
    //bout是BlockDataOutputStream实例,充当底层字节数据容器
    bout = new BlockDataOutputStream(out);
    
    //一个轻量级的哈希表,实现对象到整数值的映射
    handles = new HandleTable(10, (float) 3.00);
    
    /**
     * 一个轻量级的对象映射到替代对象的哈希表,
     * 内部由一个HandleTable示例和一个对象数组组成,
     * 通过HandlerTable实现对象到整数的映射,再通过整数从数组对象中返回替代对象
     */
    subs = new ReplaceTable(10, (float) 3.00);
    
    //如果为true序列化时调用writeObjectOverride()方法,否则调用writeObject0()方法
    enableOverride = false;
    
    //写入二进制流的头部,包括“魔数”(0xACED)和版本(0x0005)
    writeStreamHeader();
    
    //设置blkmode属性为true
    bout.setBlockDataMode(true);
    if (extendedDebugInfo) {
        debugInfoStack = new DebugTraceInfoStack();
    } else {
        debugInfoStack = null;
    }
}
  • 序列化入口: ObjectOutputStream.writeObject(Object obj)
//如果enableOverride值为true则调用writeObjectOverride(obj)方法,否则执行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;
    }
}
  • ObjectOutputStream.writeObject0(Object obj, boolean unshared)
 private void writeObject0(Object obj, boolean unshared) throws IOException {
    boolean oldMode = bout.setBlockDataMode(false);
    depth++; //递归深度加1
    try {
        // handle previously written and non-replaceable objects
        int h;
        //obj为null或者替代对象为null时调用writeNull,写入十六进制0x70
        if ((obj = subs.lookup(obj)) == null) { 
            writeNull();
            return;
        } 
        //
        else if (!unshared && (h = handles.lookup(obj)) != -1) {
            writeHandle(h);
            return;
        } 
        //待序列化的对象是java.lang.Class实例则执行writeClass,写入0x76、类描述符并把当前obj放到HandleTable里
        else if (obj instanceof Class) { 
            writeClass((Class) obj, unshared);
            return;
        } 
        /** 
         * 待序列化的对象是ObjectStreamClass实例则执行writeClassDesc,写入给定类的描述符,
         * ObjectStreamClass对象用来提取序列化过程中某个对象所属类的元数据信息,例如类名、 
         * 序列化号等等。
         */
        else if (obj instanceof ObjectStreamClass) { 
            writeClassDesc((ObjectStreamClass) obj, unshared);
            return;
        }
        
        //检查是否需要对序列化的对象进行替换序列化
        Object orig = obj;
        Class cl = obj.getClass();
        ObjectStreamClass desc;
        for (;;) {
            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;
        }

        // 如果序列化对象被替换过则需要进行与wirteObject0()开头类似的一些判断和处理
        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;
            }
        }

        //根据对象的实际类型执行不同的写入操作
        if (obj instanceof String) { //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) { //实现Serializable接口的对象序列化
            writeOrdinaryObject(obj, desc, unshared);
        } else { //不满足以上任一种情况,抛出NotSerializableException
            if (extendedDebugInfo) {
                throw new NotSerializableException(
                    cl.getName() + "\n" + debugInfoStack.toString());
            } else {
                throw new NotSerializableException(cl.getName());
            }
        }
    } finally {
        depth--;
        bout.setBlockDataMode(oldMode);
    }
}
  • 序列化String对象: ObjectOutputStream.writeString(String str, boolean unshared)
private void writeString(String str, boolean unshared) throws IOException {
    handles.assign(unshared ? null : str); //放入HandleTable
    long utflen = bout.getUTFLength(str);
    if (utflen <= 0xFFFF) { //0xFFFF = 65535
        bout.writeByte(TC_STRING); //0x74
        bout.writeUTF(str, utflen); //以UTF格式写入二进制流,先写入长度,再写入字符串内容
    } else {
        bout.writeByte(TC_LONGSTRING); //0x7C
        bout.writeLongUTF(str, utflen); //同样以UTF格式先写入长度再写入内容
    }
}
  • 序列化数组对象: ObjectOutputStream.writeArray(Object obj, ObjectStreamClass desc, boolean unshared)
private void writeArray(Object array, ObjectStreamClass desc, boolean unshared) throws IOException {
    bout.writeByte(TC_ARRAY); //0x75
    writeClassDesc(desc, false); //写入类描述符
    handles.assign(unshared ? null : array); //将array对象放入HandleTable

    Class ccl = desc.forClass().getComponentType(); //获取数组的类型
    //根据数组的实际类型执行不同的写入操作
    if (ccl.isPrimitive()) { //原始数据类型及其对应的包装类
        if (ccl == Integer.TYPE) {
            int[] ia = (int[]) array;
            bout.writeInt(ia.length);
            bout.writeInts(ia, 0, ia.length);
        } else if (ccl == Byte.TYPE) {
            byte[] ba = (byte[]) array;
            bout.writeInt(ba.length);
            bout.write(ba, 0, ba.length, true);
        } else if (ccl == Long.TYPE) {
            long[] ja = (long[]) array;
            bout.writeInt(ja.length);
            bout.writeLongs(ja, 0, ja.length);
        } else if (ccl == Float.TYPE) {
            float[] fa = (float[]) array;
            bout.writeInt(fa.length);
            bout.writeFloats(fa, 0, fa.length);
        } else if (ccl == Double.TYPE) {
            double[] da = (double[]) array;
            bout.writeInt(da.length);
            bout.writeDoubles(da, 0, da.length);
        } else if (ccl == Short.TYPE) {
            short[] sa = (short[]) array;
            bout.writeInt(sa.length);
            bout.writeShorts(sa, 0, sa.length);
        } else if (ccl == Character.TYPE) {
            char[] ca = (char[]) array;
            bout.writeInt(ca.length);
            bout.writeChars(ca, 0, ca.length);
        } else if (ccl == Boolean.TYPE) {
            boolean[] za = (boolean[]) array;
            bout.writeInt(za.length);
            bout.writeBooleans(za, 0, za.length);
        } else {
            throw new InternalError();
        }
    } else { //引用类型数组的处理:写入数组长度,遍历数组依次写入每个对象元素
        Object[] objs = (Object[]) array;
        int len = objs.length;
        bout.writeInt(len);
        if (extendedDebugInfo) {
            debugInfoStack.push(
                "array (class \"" + array.getClass().getName() +
                "\", size: " + len  + ")");
        }
        try {
            for (int i = 0; i < len; i++) {
                if (extendedDebugInfo) {
                    debugInfoStack.push(
                        "element of array (index: " + i + ")");
                }
                try {
                    writeObject0(objs[i], false);
                } finally {
                    if (extendedDebugInfo) {
                        debugInfoStack.pop();
                    }
                }
            }
        } finally {
            if (extendedDebugInfo) {
                debugInfoStack.pop();
            }
        }
    }
}
  • 序列化枚举类型对象: ObjectOutputStream.writeEnum(Enum en, ObjectStreamClass desc, boolean unshared)
private void writeEnum(Enum en, ObjectStreamClass desc, boolean unshared) throws IOException {
    bout.writeByte(TC_ENUM); //0x7E
    ObjectStreamClass sdesc = desc.getSuperDesc();
    writeClassDesc((sdesc.forClass() == Enum.class) ? desc : sdesc, false);
    handles.assign(unshared ? null : en);
    writeString(en.name(), false); //序列化枚举对象的name属性
}
  • 序列化对象:ObjectOutputStream.writeOrdinaryObject(Object obj,ObjectStreamClass desc, boolean unshared)
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 {
        //如果该类描述符表示的对象不允许序列化,将抛出InvalidClassException
        desc.checkSerialize();
        bout.writeByte(TC_OBJECT); //0x73
        writeClassDesc(desc, false);
        handles.assign(unshared ? null : obj);
        //如果该对象实现了java.io.Externalizable接口并且不是动态代理产生的对象,则调用writeExternalData方法
        if (desc.isExternalizable() && !desc.isProxy()) {
            writeExternalData((Externalizable) obj);
        } else { //否则调用writeSerialData()序列化对象
            writeSerialData(obj, desc);
        }
    } finally {
        if (extendedDebugInfo) {
            debugInfoStack.pop();
        }
    }
}
  • 序列化对象的实例数据,包括父类到子类: ObjectOutputStream.writeSerialData(Object obj, ObjectStreamClass desc)
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;
        //如果被序列化对象复写了writeObject方法则执行if块的逻辑,否则执行defaultWriteFields方法
        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 {
            defaultWriteFields(obj, slotDesc);
        }
    }
}
  • 序列化默认的实例数据: ObjectOutputStream.defaultWriteFields(Object obj, ObjectStreamClass desc)
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();
    //准备一个字节数组primVals
    int primDataSize = desc.getPrimDataSize();
    if (primVals == null || primVals.length < primDataSize) {
        primVals = new byte[primDataSize];
    }
    //获取对象中的基本数据类型的数据并保存在primVals字节数组中
    desc.getPrimFieldValues(obj, primVals);
    //将基本数据类型的数据写入底层字节容器中
    bout.write(primVals, 0, primDataSize, false);
    
    /** 获取对应类的所有成员变量,ObjectStreamField类主要用来提取序列化过程中某个对象内的字段的元数据
     * 信息,例如字段的类型、类型代码、签名等等
     */
    ObjectStreamField[] fields = desc.getFields(false);
    //准备一个对象数组用来存放引用类型的成员变量
    Object[] objVals = new Object[desc.getNumObjFields()];
    int numPrimFields = fields.length - objVals.length;
    desc.getObjFieldValues(obj, objVals);
    //遍历对象数组,递归对引用类型的字段调用writeObject0()方法进行序列化
    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();
            }
        }
    }
}

/**
 * 以之前的代码示例为例,执行writeObjcet方法会执行到该方法,因此:
 * 1. 获取son对象的基本类型的字段数据,并写入到底层的字节容器中
 * 2. 获取son对象的引用类型字段数据,即parent对象,递归调用writeObjcet0()进行序列化
 * 3. 如果Person类有父类,会一并获取父类的字段数据,写入顺序按照先父类后子类写入。
 */

几点说明: 1. 序列化开始时,首先写入固定的magic number和版本号。对应temp.out开头的AC ED 00 05 2. 根据待序列化对象或替代对象的类型分别执行不同的写入方法,如null、原始类型、引用类型。 3. 写入引用类型对象时,会写入对象类型声明标志位、类描述声明标记位、类描述信息(包括类名长度、序列号、serialVersionUID值、支持序列化标记、字段个数等等),然后从下到上递归序列化其父类的类描述信息。再之后是从父类中的成员属性开始,从上到下递归序列化成员变量的信息。 4. 不管序列化哪些信息,都会在之前先写入相应的标志位,标识这是一个对象、一个描述符、数据长度亦或是数据类型等等。

temp.out文件解读

参照以上源码,可以知道temp.out输出文件的每1个或多个字节具体代表哪些信息:

  • temp.out文件内容
AC	ED	00	05	73	72	00	10	63	6F	6D	2E	6C	69	61	6E
67	2E	50	65	72	73	6F	6E	00	00	00	00	00	00	00	01
02	00	03	49	00	03	61	67	65	4C	00	04	6E	61	6D	65
74	00	12	4C	6A	61	76	61	2F	6C	61	6E	67	2F	53	74
72	69	6E	67	3B	4C	00	06	70	61	72	65	6E	74	74	00
12	4C	63	6F	6D	2F	6C	69	61	6E	67	2F	50	65	72	73
6F	6E	3B	78	70	00	00	00	0A	74	00	08	78	69	61	6F
6D	69	6E	67	73	71	00	7E	00	00	00	00	00	28	74	00
06	64	61	6D	69	6E	67	70

每两个十六进制为一个字节:

  • AC ED: 魔数。
  • 00 05: version值。
  • 73: TC_OBJECT,标记接下来写入的是一个新Object对象。
  • 72:TC_CLASSDESC,标记接下来写入的是类描述符信息。
  • 00 10:类名长度,com.liang.Person的长度16
  • 63 6F 6D 2E 6C 69 61 6E 67 2E 50 65 72 73 6F 6E:类名,字符串”com.liang.Person”的十六进制表示
  • 00 00 00 00 00 00 00 01:serialVersionUID,这里是1L。
  • 02:可序列化标志,当类实现了Serializable会写入该标志位
  • 00 03:序列化对象的成员属性个数,这里指name、age、parent三个成员变量。序列化成员属性时顺序是以变量名的字典序顺序来进行,所以序列化顺序是: age -> name -> parent。并且序列化成员变量会先写入字段类型的字符编码,字段类型与字符编码的映射关系如下:
byte –> B, char->C, double->D, float->F, 
int->I,    long->J, short->S,  boolean->Z	
类或者接口->L, 数组->[
  • 49:age字段类型的字符编码,age为int型对应写入编码为I,即16进制49。
  • 00 03:字段名的长度,age长度为3。
  • 61 67 65:字段名,字符串”age”的十六进制表示。
  • 4C:name字段类型的字符编码,name为String型对应字符编码为L,转为16进制为4C。
  • 00 04:name字段名的长度4.
  • 6E 61 6D 65:字符串”name”的十六进制表示
  • 74:TC_STRING,标记接下来写入的是一个String。
  • 00 12:接下来写入的String的长度为十六进制的0012,换算成十进制就是18。
  • 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B:当成员属性不是基本数据类型时,会写入字段类型在JVM中的签名。name是String类型,因此这里写入字符串’Ljava/lang/String;’的十六进制表示。
  • 49:parent字段类型的字符编码,parent为对象类型,写入I的十六进制表示。
  • 00 06:字段名parent的长度6。
  • 70 61 72 65 6E 74:字段名”parent”的十六进制表示。
  • 74:TC_STRING,标记接下来写入的是一个String。
  • 00 12:接下来写入的字符串” Lcom/liang/Person;”的长度为18。
  • 4C 63 6F 6D 2F 6C 69 61 6E 67 2F 50 65 72 73 6F 6E 3B:写入字段类型签名”Lcom/liang/Person;”
  • 78:TC_ENDBLOCKDATA,标记对象数据块的结束,到这里son对象本身的数据块就序列化完成了,接下来如果son对象所属的类如果有父类,将会对父类的相关信息进行序列化,如果没有父类也会写入标记标识没有父类。
  • 70:TC_NULL,这里标识Person类没有除Object以外的父类。

接下来将会为从父类到子类的顺序,为每个可序列化的对象写入实例数据信息,即保存对象序列化时的状态。

  • 00 00 00 0A:son对象中age属性的值,换算成十进制即为10。
  • 74:TC_STRING,接下来写入的属性值是一个String。
  • 00 08:该字符串的长度为8。
  • 78 69 61 6F 6D 69 6E 67:son对象中name属性的值,即’xiaoming’。
  • 73:TC_OBJECT,接下来写入parent变量的值,parent是一个对象,因此需要写入TC_OBJECT标记位。
  • 71:TC_REFERENCE,因为parent所属的类com.liang.Person信息已经写入过流了,不需要再一次序列化,所以这里写入TC_REFERENCE标志。
  • 00 7E 00 00:一个固定的常量,表示第一个赋值的句柄。
  • 00 00 00 28: parent对象的成员属性age的值40。
  • 74:TC_STRING,接下来写入的属性值是一个String。
  • 00 06:该字符串的长度为6。
  • 64 61 6D 69 6E 67:parent对象中name属性的值,即’daming’。
  • 70:TC_NULL,这里标识parent对象的parent属性值为null。

反序列化简单分析

反序列化入口函数为ObjectInputStream.readObject(),该方法作用只是判断应该调用readObjectOverride()还是readObject0(boolean),然后在反序列化完成后,调用vlist成员变量的doCallbacks()方法执行回调逻辑。

下面主要分析readObject0方法:

 private Object readObject0(boolean unshared) throws IOException {
    //检查是否采用Data Block模式读取
    boolean oldMode = bin.getBlockDataMode();
    if (oldMode) { //采用Data Block模式读取
        //计算字节流中剩余字节数,大于0或者没有defaultDataEnd的值为true则抛出OptionalDataException
        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;
    //如果字节流中包含TC_RESET标记,调用handleReset()方法
    while ((tc = bin.peekByte()) == TC_RESET) {
        bin.readByte();
        handleReset();
    }

    depth++;
    totalObjectRefs++;
    try {
        /** 根据读取的标记执行反序列化操作;
         *  这里需要注意的一个点是当标记为TC_ARRAY、TC_ENUM、TC_OBJECT、TC_STRING、TC_LONGSTRING时
         *  会对读取的返回结果调用checkResolve方法,此时如果被反序列化的对象重写了readResolve()方法
         *  那么就会执行readResolve()方法,该方法可以用来防止单例对象通过序列化反序列化的方式破坏单
         *  例模式。
         */
        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);
    }
}

从readObject0方法我们没有看到解析魔数和序列化版本号的方法,这是因为这一步操作是在实例化ObjectInputStream的构造函数中进行:

public ObjectInputStream(InputStream in) throws IOException {
    ......
    readStreamHeader();
    ......
}

protected void readStreamHeader() throws IOException, StreamCorruptedException {
    short s0 = bin.readShort();
    short s1 = bin.readShort();
    if (s0 != STREAM_MAGIC || s1 != STREAM_VERSION) {
        throw new StreamCorruptedException(
            String.format("invalid stream header: %04X%04X", s0, s1));
    }
}

转载于:https://my.oschina.net/u/3761681/blog/1810736

你可能感兴趣的:(Java序列化)