(从09年回到重庆过后,就一直在工作,时间长了惰性就慢慢起来了,公司的项目从09年忙到了现在,一直没有时间来梳理自己的东西,CSDN的Blog似乎都荒废了,不知道现在还能否坚持把Blog完成,希望有一个新的开始吧!如果读者有问题还是可直接发邮件到silentbalanceyh@126.com,我也仅仅只是想把看的、写的、学的东西总结起来,让自己有个比较完整的学习记录。本文主要针对Java的序列化相关知识,先不涉及XML序列化和Json序列化的内容,这部分内容以后再议。着色的目的是强调重点和关键的概念以及防止读者通篇阅读的视觉疲劳,也是个人的写作风格,不习惯的读者请见谅!——本来打算把全文的内容放在一篇Blog中来讲解,慢慢地就发现Java序列化的内容其实蛮多的,篇幅长了过后排版慢慢变得比较复杂,写这么多的目的就是为了把我自己理解过、分析过、验证过以及开发的时候用过的内容分享给大家,也许我不能保证在CSDN上频繁地发BLOG,但每发一次尽可能保证这些内容的营养价值。)
本章目录:
1.Java中的序列化
2.序列化原理和算法——基础数据
3.深入序列化规范
4.源码分析
----ObjectStreamField
----ObjectStreamClass
----ObjectOutputStream
----ObjectInputStream
5.序列化原理和算法——面向对象
4.源码分析
在讲解本章的内容之前,先简单回顾一下:前文讲了Java中的序列化的基本概念,并且通过输出的二进制文件内容的分析理解了Java序列化的原理和算法,同样引领读者解析了JVM的序列化规范;第二章中我们分析了ObjectStreamField和ObjectStreamClass两个类的源代码。
其实迄今为止,所有的讲解和分析都是从一个角度在阐述——数据结构。二进制文件内容的分析实际上是让读者去理解Java序列化生成的目标数据是什么结构,而ObjectStreamField和ObjectStreamClass两个类主要用途就是用于描述对象所属类的元数据以及它所定义的成员属性的相关元数据,它们的源码中除了ObjectStreamClass中的readNonProxy和writeNonProxy包含了和序列化和反序列化执行过程的内容以外,其他所有的内容都是和数据结构相关的东西。
前边的章节我们一直讨论了下边几个问题:
- Java的序列化是什么?
- Java中内建序列化生成的二进制数据是什么结构?
- 基础类型的数据在不同的情况会如何写入到二进制文件中?
- Java的序列化规范中有些什么关键性概念?
- Java中序列化部分的源代码定义了一些什么数据结构模型?【ObjectStreamClass&ObjectStreamField】?
- 详细分析Java序列化中使用的几个核心类的源代码,通过其内部执行流程换一种角度来看看Java序列化的知识;
- 针对JVM的序列化规范中的一些疑点提供示例来加以证明,进行规范的深入理解;
- 提供Java序列化中和面向对象、最佳实践的相关内容;
- ObjectStreamField
- ObjectStreamClass
- ObjectOutputStream
- ObjectInputStream
iii.ObjectOutputStream源码分析
ObjectOutputStream是Java序列化机制中负责序列化的主类,在使用它的时候会用到前文提及的ObjectStreamClass以及ObjectStreamField两个核心类,接下来我们一步一步去揭开这个类的面纱:
1)类定义:
ObjectOutputStream的完整类定义如下:
- public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants
从ObjectOutputStream的定义可以知道,它有一个父类OutputStream,实现了两个接口ObjectOutput以及ObjectStreamConstants。Java序列化中包含了很多常量值,其中前文提到的最多的是TC_*标记和SC_*标记,这些标记都是在接口ObjectStreamConstants中定义的,所以它们都属于接口常量。不仅仅是ObjectOutputStream实现了该接口,反序列化的主类ObjectInputStream同样也实现了该接口。父类OutputStream以及接口ObjectOutput的职责如下:
- OutputStream是一个抽象类,它表示所有需要输出字节流的类的超类,输出流接受输出字节并将这些字节发送到某个接收器,所以它的子类的应用程序必须始终提供至少一种可写入输出字节的方法;
- ObjectOutput扩展了DataOutput接口以包含对象的写入操作,DataOutput本身只包含了基础类型的输出方法,而ObjectOutput扩展了该接口,它包含了对象、数组以及String的输出方法;
- private static class Caches
- public static abstract class PutField
- private class PutFieldImpl extends PutField
- private static class BlockDataOutputStream extends OutputStream implements DataOutput
- private static class HandleTable
- private static class ReplaceTable
- private static class DebugTraceInfoStack
Caches【缓存类】
该类提供了序列化中的缓存机制,它只包含了两个固定的成员属性,其完整定义如下:
- private static class Caches {
- /** cache of subclass security audit results */
- static final ConcurrentMap<WeakClassKey,Boolean> subclassAudits =
- new ConcurrentHashMap<>();
- /** queue for WeakReferences to audited subclasses */
- static final ReferenceQueue<Class<?>> subclassAuditsQueue =
- new ReferenceQueue<>();
- }
Caches类中包含了两个成员subclassAudits和subclasseAuditsQueue:
subclassAudits——该成员属性提供了一个哈希表缓存,该缓存的键类型为java.io.ObjectStreamClass.WeakClassKey,注意看它的值类型是一个java.lang.Boolean类型的,从其代码注释可以知道这个哈希表缓存中保存的是所有子类的代码执行安全性检测结果;
subclassAuditsQueue——该成员属性定义了一个“Queue队列”,它的用法和前文中ObjectStreamClass.Caches中“Queue”的用法是一致的;
PutField:
PutField类为一个抽象类,它提供对要写入ObjectOutput的持久字段的编程访问,先看看它的源代码:
- public static abstract class PutField {
- public abstract void put(String name, boolean val);
- public abstract void put(String name, byte val);
- public abstract void put(String name, char val);
- public abstract void put(String name, short val);
- public abstract void put(String name, int val);
- public abstract void put(String name, long val);
- public abstract void put(String name, float val);
- public abstract void put(String name, double val);
- public abstract void put(String name, Object val);
- @Deprecated
- public abstract void write(ObjectOutput out) throws IOException;
- }
PutField抽象类中主要有9个put方法,以及一个已经过期的write方法,put方法主要将对应类型的字段值写入到持久化字段中,9个方法分别对应Java语言中的8个基础类型以及1个Object类型的字段的写入。它的参数如下:
- name——java.lang.String
一个类中定义的可序列化的字段名称; - val
需要赋值给该字段的值,它的类型不一样则调用的方法就会不同,该类中定义的put方法根据参数类型不同而实现重载;
- /** class descriptor describing serializable fields */
- private final ObjectStreamClass desc;
- /** primitive field values */
- private final byte[] primVals;
- /** object field values */
- private final Object[] objVals;
- desc——java.io.ObjectStreamClass
该成员属性用于描述Java对象所属类的元数据信息; - primVals——byte[]
该字节数组用于保存基础数据类型的值; - objVals——java.lang.Object[]
该Object类型的数组用于保存对象数据类型的值;
- PutFieldImpl(ObjectStreamClass desc) {
- this.desc = desc;
- primVals = new byte[desc.getPrimDataSize()];
- objVals = new Object[desc.getNumObjFields()];
- }
PutFieldImpl类的构造函数拥有一个参数desc,该参数的类型为java.io.ObjectStreamClass,PutFieldImpl实现类会从该类的元数据中提取成员属性信息;一方面desc引用会赋值该成员属性desc,然后通过类的元数据中的字段信息初始化primVals字节数组以及objVals对象数组。区分ObjectStreamField类和PutFieldImpl类对字段元数据的描述:ObjectStreamField描述的是成员属性的元数据信息【字段定义】,而PutFieldImpl描述的是成员属性的数据信息【字段使用】。
除了已过期的write方法,PutFieldImpl实现类中提供了另外两个新定义的成员函数
writeFields和getFieldOffset:
- void writeFields() throws IOException {
- bout.write(primVals, 0, primVals.length, false);
- ObjectStreamField[] fields = desc.getFields(false);
- int numPrimFields = fields.length - objVals.length;
- 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();
- }
- }
- }
- }
- private int getFieldOffset(String name, Class type) {
- ObjectStreamField field = desc.getField(name, type);
- if (field == null) {
- throw new IllegalArgumentException("no such field " + name +
- " with type " + type);
- }
- return field.getOffset();
- }
这里不解析write方法的用法,重点看看子类中新定义的两个成员函数:
writeFields
该方法负责将基础类型数据的值和对象类型数据的值写入字节流,看看它的实现细节:
- 先将被解析类中基础类型数据的数量信息写入到字节缓冲区,写入字节流的时候调用了ObjectOutputStream主类的bout成员:
- bout.write(primVals, 0, primVals.length, false);
- 调用desc成员属性的getFields方法获得被解析类中的所有字段元数据信息,注意getFields方法的参数为false,它表示在获取ObjectStreamField的时候,里面的每一个元素引用指向的字段元数据对象是原始对象,而不是一个副本【拷贝】:
- ObjectStreamField[] fields = desc.getFields(false);
- 将字段中的数据写入到字节流中,这段代码调用了主类中的writeObject0成员函数;看看下边代码中的Debug段,它使用了主类中的成员属性extendedDebugInfo,并且结合成员属性debugInfoStack来实现启用/禁用Debug功能:
- int numPrimFields = fields.length - objVals.length;
- 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();
- }
- }
- }
该成员函数根据字段名称和字段类型来获得该字段在所定义的类中的偏移量offset,它有2个参数:
- name——java.lang.String
字段名称
- type——java.lang.Class
字段类型
- public void put(String name, boolean val) {
- Bits.putBoolean(primVals, getFieldOffset(name, Boolean.TYPE), val);
- }
- public void put(String name, byte val) {
- primVals[getFieldOffset(name, Byte.TYPE)] = val;
- }
- public void put(String name, char val) {
- Bits.putChar(primVals, getFieldOffset(name, Character.TYPE), val);
- }
- public void put(String name, short val) {
- Bits.putShort(primVals, getFieldOffset(name, Short.TYPE), val);
- }
- public void put(String name, int val) {
- Bits.putInt(primVals, getFieldOffset(name, Integer.TYPE), val);
- }
- public void put(String name, float val) {
- Bits.putFloat(primVals, getFieldOffset(name, Float.TYPE), val);
- }
- public void put(String name, long val) {
- Bits.putLong(primVals, getFieldOffset(name, Long.TYPE), val);
- }
- public void put(String name, double val) {
- Bits.putDouble(primVals, getFieldOffset(name, Double.TYPE), val);
- }
- public void put(String name, Object val) {
- objVals[getFieldOffset(name, Object.class)] = val;
- }
第一种是直接赋值【byte类型、Object类型】
看看为什么?成员属性primVals本身就是一个byte[]类型,这种类型的每一个元素都是一个byte,所以针对byte数据写入的时候,可以使用下边的代码来实现:
- primVals[getFieldOffset(name, Byte.TYPE)] = val;
- objVals[getFieldOffset(name, Object.class)] = val;
类似下边这种代码:
- Bits.putLong(primVals, getFieldOffset(name, Long.TYPE), val);
成员属性
- private final List<String> stack;
- stack——java.util.List<String>
该成员保存了所有调试过程中的堆栈信息,使用的数据类型为一个String列表;
- private static class DebugTraceInfoStack {
- private final List<String> stack;
- DebugTraceInfoStack() {
- stack = new ArrayList<>();
- }
- void clear() {
- stack.clear();
- }
- void pop() {
- stack.remove(stack.size()-1);
- }
- void push(String entry) {
- stack.add("\t- " + entry);
- }
- public String toString() {
- StringBuilder buffer = new StringBuilder();
- if (!stack.isEmpty()) {
- for(int i = stack.size(); i > 0; i-- ) {
- buffer.append(stack.get(i-1) + ((i != 1) ? "\n" : ""));
- }
- }
- return buffer.toString();
- }
- }
- /* number of mappings in table/next available handle */
- private int size;
- /* size threshold determining when to expand hash spine */
- private int threshold;
- /* factor for computing size threshold */
- private final float loadFactor;
- /* maps hash value -> candidate handle value */
- private int[] spine;
- /* maps handle value -> next candidate handle value */
- private int[] next;
- /* maps handle value -> associated object */
- private Object[] objs;
- size——int
哈希表中的映射的数量; - treshold——int
确定何时扩大哈希表的阀值,一旦存储的数量超过了哈希表的容量则会扩充该哈希表的容量; - loadFactor——float
用于计算阀值的计算因子,这个因子的定义是一个常量值,使用了final修饰符; - spine——int[]
当前哈希表中保存的整数类型的引用Handle值; - next——int[]
下一个哈希表中保存的整数类型的引用Handle值; - objs——java.lang.Object[]
和对应的引用Handle相关联的对象的值;
- HandleTable(int initialCapacity, float loadFactor) {
- this.loadFactor = loadFactor;
- spine = new int[initialCapacity];
- next = new int[initialCapacity];
- objs = new Object[initialCapacity];
- threshold = (int) (initialCapacity * loadFactor);
- clear();
- }
- initialCapacity——int
当前哈希表构造时的初始容量,如果在操作该哈希表的过程中数据容量超过了初始化的容量,则需要对哈希表进行扩容操作; - loadFactor——float
当前哈希表构造时计算阀值的因子,该因子一旦赋值给成员属性loadFactor过后就不可更改了,针对每一个哈希表而言它计算阀值的因子是一个固定值;
- int assign(Object obj) {
- if (size >= next.length) {
- growEntries();
- }
- if (size >= threshold) {
- growSpine();
- }
- insert(obj, size);
- return size++;
- }
- int lookup(Object obj) {
- if (size == 0) {
- return -1;
- }
- int index = hash(obj) % spine.length;
- for (int i = spine[index]; i >= 0; i = next[i]) {
- if (objs[i] == obj) {
- return i;
- }
- }
- return -1;
- }
- void clear() {
- Arrays.fill(spine, -1);
- Arrays.fill(objs, 0, size, null);
- size = 0;
- }
- int size() {
- return size;
- }
- private void insert(Object obj, int handle) {
- int index = hash(obj) % spine.length;
- objs[handle] = obj;
- next[handle] = spine[index];
- spine[index] = handle;
- }
- private void growSpine() {
- spine = new int[(spine.length << 1) + 1];
- threshold = (int) (spine.length * loadFactor);
- Arrays.fill(spine, -1);
- for (int i = 0; i < size; i++) {
- insert(objs[i], i);
- }
- }
- private void growEntries() {
- int newLength = (next.length << 1) + 1;
- int[] newNext = new int[newLength];
- System.arraycopy(next, 0, newNext, 0, size);
- next = newNext;
- Object[] newObjs = new Object[newLength];
- System.arraycopy(objs, 0, newObjs, 0, size);
- objs = newObjs;
- }
- private int hash(Object obj) {
- return System.identityHashCode(obj) & 0x7FFFFFFF;
- }
- clear
该方法用于将当前哈希表清空,它调用了Arrays的fill方法,并且设置成员函数size为0;因为该函数调用的时候,spine字节数组中的每一个元素已经有值了,所以将每一个元素的值填充为-1;而objs中的对象也有值了,所以将索引范围0到size的对象元素全部设置成null;从这一点可以知道在调用clear函数的时候清空成员属性spine、objs数组的方式使用的是“值填充”方式; - size
该方法返回当前哈希表中已经存在的映射数量; - growSpine
该方法用于哈希表的容量扩充,主要针对objs、spine两个数组,其执行流程如下:
a.放弃成员spine的原始数组,将扩充过后的容量为“原始容量 * 2 + 1”并且初始化一个新的数组,例如原始长度为20则扩充过后为41;
b.重新计算阀值threshold;
c.将新数组中的每一个成员都初始化成-1;
d.设置objs数组中原始容量中的对象值; - getEntries
该方法同样用于哈希表的容量扩充,但主要是针对objs、next两个数组,也就是针对对象数据:
a.先计算扩充过后的新容量;
b.该方法的调用过程使用了System.arraycopy方法,该方法从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束; - hash
该方法用于返回一个固定的hash值,它的最终结果值为:System.identityHashCode( obj ) & 0x7FFFFFFF; - insert
该方法将根据当前Java对象和整数引用Handle来设置成员属性objs、next、spine的值; - assign
该方法根据下一个Java对象和整数引用Handle来设置成员属性objs、next、spine的值,该方法调用了insert方法,如果容量不够的时候,该方法会自动扩充哈希表的容量; - lookup
该方法用于查找和传入对象匹配的引用Handle,如果找不到则返回-1;
- private static class ReplaceTable {
- /* maps object -> index */
- private final HandleTable htab;
- /* maps index -> replacement object */
- private Object[] reps;
- /**
- * Creates new ReplaceTable with given capacity and load factor.
- */
- ReplaceTable(int initialCapacity, float loadFactor) {
- htab = new HandleTable(initialCapacity, loadFactor);
- reps = new Object[initialCapacity];
- }
- /**
- * Enters mapping from object to replacement object.
- */
- void assign(Object obj, Object rep) {
- int index = htab.assign(obj);
- while (index >= reps.length) {
- grow();
- }
- reps[index] = rep;
- }
- /**
- * Looks up and returns replacement for given object. If no
- * replacement is found, returns the lookup object itself.
- */
- Object lookup(Object obj) {
- int index = htab.lookup(obj);
- return (index >= 0) ? reps[index] : obj;
- }
- /**
- * Resets table to its initial (empty) state.
- */
- void clear() {
- Arrays.fill(reps, 0, htab.size(), null);
- htab.clear();
- }
- /**
- * Returns the number of mappings currently in table.
- */
- int size() {
- return htab.size();
- }
- /**
- * Increases table capacity.
- */
- private void grow() {
- Object[] newReps = new Object[(reps.length << 1) + 1];
- System.arraycopy(reps, 0, newReps, 0, reps.length);
- reps = newReps;
- }
- }
a.在默认模式下,写入数据的时候使用和DataOutputStream相同的模式;java.io.DataOutputStream是JVM中负责将一个基础类型数据写入到输出流中的类,该类称为基础类型数据输出流,只是该类不仅仅使用于Java序列化,如果是流数据的读写,也可以使用java.io.DataOutputStream和java.io.DataInputStream两个类,因为它们的内部结构相对简单,本文不解析这两个类的源代码。
- <span style="font-size:12px;"> private static class BlockDataOutputStream extends OutputStream implements DataOutput</span>
- /** maximum data block length */
- private static final int MAX_BLOCK_SIZE = 1024;
- /** maximum data block header length */
- private static final int MAX_HEADER_SIZE = 5;
- /** (tunable) length of char buffer (for writing strings) */
- private static final int CHAR_BUF_SIZE = 256;
- /** buffer for writing general/block data */
- private final byte[] buf = new byte[MAX_BLOCK_SIZE];
- /** buffer for writing block data headers */
- private final byte[] hbuf = new byte[MAX_HEADER_SIZE];
- /** char buffer for fast string writes */
- private final char[] cbuf = new char[CHAR_BUF_SIZE];
- /** block data mode */
- private boolean blkmode = false;
- /** current offset into buf */
- private int pos = 0;
- /** underlying output stream */
- private final OutputStream out;
- /** loopback stream (for data writes that span data blocks) */
- private final DataOutputStream dout;
BlockDataOutputStream类包含了3个静态常量:
- MAX_BLOCK_SIZE——int
当输出流使用“Data Block”模式写入基础类型数据到字节流时,则该静态常量存储了每一个Data Block的边界值1024——它表示使用Data Block写入基础类型数据时每一个Data Block的最大长度不可超越这个值; - MAX_HEADER_SIZE——int
当输出流使用“Data Block”模式写入基础类型数据到字节流时,则该静态常量表示每一个Data Block的头部值不可超过长度5; - CHAR_BUF_SIZE——int
当输出流写入字符串到字节流时,该静态常量表示一个字符缓冲区的最大长度,默认值为256;
- buf——byte[]
写入常用数据和Data Block数据使用的缓冲区,使用MAX_BLOCK_SIZE初始化数组长度; - hbuf——byte[]
写入Data Block数据的Data Block头部值的缓冲区,使用MAX_HEADER_SIZE初始化数组长度; - cbuf——char[]
写入字符串数据的缓冲区,使用CHAR_BUF_SIZE初始化数组长度;
- blkmode——boolean
该成员属性标识当前的写入模式是否使用了Data Block的写入模式,为true则表示写入模式为Data Block,反之为false,默认情况并没使用Data Block模式; - pos——int
该成员函数用于表示在一个写入缓冲区中的偏移量,Java序列化时每写入一个数据时需要使用偏移量在缓冲区中提取准确的二进制序列段;
- out——java.io.OutputStream
该成员属性表示“潜在输出流”【underlying output stream】,用于控制输出数据到字节流的主类; - dout——java.io.DataOutputStream
该成员属性表示使用Data Block模式时的主要输出流的主类【loopback stream】;
- BlockDataOutputStream(OutputStream out) {
- this.out = out;
- dout = new DataOutputStream(this);
- }
-----------------Data Block模式设置-------------
- boolean setBlockDataMode(boolean mode) throws IOException {
- if (blkmode == mode) {
- return blkmode;
- }
- drain();
- blkmode = mode;
- return !blkmode;
- }
- boolean getBlockDataMode() {
- return blkmode;
- }
上边两个成员方法用于设置Data Block模式以及获取Data Block模式,看看代码细节,关于Data Block模式的设置中,如果当前模式和传入模式是相等的则不需要设置。
-----------------DataOutput接口方法-------------
DataOutput主要用于将基础数据转换成字节数据写入,该接口的整体信息如下:
因为BlockDataOutputStream实现了这些方法,所以这里先看看这些方法的实现细节:
write方法
该类中包含了三个write的基本方法:
- public void write(int b) throws IOException {
- if (pos >= MAX_BLOCK_SIZE) {
- drain();
- }
- buf[pos++] = (byte) b;
- }
- public void write(byte[] b) throws IOException {
- write(b, 0, b.length, false);
- }
- public void write(byte[] b, int off, int len) throws IOException {
- write(b, off, len, false);
- }
上边这三个基本方法主要用于写入字节,除开这三个方法的基本写入以外,该类中还定义了另外一个write方法,该方法是write方法的重载,其定义如下:
- void write(byte[] b, int off, int len, boolean copy)
- throws IOException
- {
- if (!(copy || blkmode)) { // write directly
- drain();
- out.write(b, off, len);
- return;
- }
- while (len > 0) {
- if (pos >= MAX_BLOCK_SIZE) {
- drain();
- }
- if (len >= MAX_BLOCK_SIZE && !copy && pos == 0) {
- // avoid unnecessary copy
- writeBlockHeader(MAX_BLOCK_SIZE);
- out.write(b, off, MAX_BLOCK_SIZE);
- off += MAX_BLOCK_SIZE;
- len -= MAX_BLOCK_SIZE;
- } else {
- int wlen = Math.min(len, MAX_BLOCK_SIZE - pos);
- System.arraycopy(b, off, buf, pos, wlen);
- pos += wlen;
- off += wlen;
- len -= wlen;
- }
- }
- }
write*方法
——基础类型写入
- public void writeBoolean(boolean v) throws IOException {
- if (pos >= MAX_BLOCK_SIZE) {
- drain();
- }
- Bits.putBoolean(buf, pos++, v);
- }
- public void writeByte(int v) throws IOException {
- if (pos >= MAX_BLOCK_SIZE) {
- drain();
- }
- buf[pos++] = (byte) v;
- }
- public void writeChar(int v) throws IOException {
- if (pos + 2 <= MAX_BLOCK_SIZE) {
- Bits.putChar(buf, pos, (char) v);
- pos += 2;
- } else {
- dout.writeChar(v);
- }
- }
- public void writeShort(int v) throws IOException {
- if (pos + 2 <= MAX_BLOCK_SIZE) {
- Bits.putShort(buf, pos, (short) v);
- pos += 2;
- } else {
- dout.writeShort(v);
- }
- }
- public void writeInt(int v) throws IOException {
- if (pos + 4 <= MAX_BLOCK_SIZE) {
- Bits.putInt(buf, pos, v);
- pos += 4;
- } else {
- dout.writeInt(v);
- }
- }
- public void writeFloat(float v) throws IOException {
- if (pos + 4 <= MAX_BLOCK_SIZE) {
- Bits.putFloat(buf, pos, v);
- pos += 4;
- } else {
- dout.writeFloat(v);
- }
- }
- public void writeLong(long v) throws IOException {
- if (pos + 8 <= MAX_BLOCK_SIZE) {
- Bits.putLong(buf, pos, v);
- pos += 8;
- } else {
- dout.writeLong(v);
- }
- }
- public void writeDouble(double v) throws IOException {
- if (pos + 8 <= MAX_BLOCK_SIZE) {
- Bits.putDouble(buf, pos, v);
- pos += 8;
- } else {
- dout.writeDouble(v);
- }
- }
上边三个方法分别负责写入8个基础类型的数据,其写入过程中,注意写入的判断条件:检查其写入过后的偏移量是否越过了Data Block的最大值MAX_BLOCK_SIZE,如果越过了直接调用dout的write*对应方法,未越过的情况针对不同数据类型分别写入,并且修改缓冲区中的偏移量——注:每写入一个数据其偏移量的改变值会根据数据类型来,所以pos += 后边的数字为传入数据类型对应所占字节数;
——String写入
- public void writeBytes(String s) throws IOException {
- int endoff = s.length();
- int cpos = 0;
- int csize = 0;
- for (int off = 0; off < endoff; ) {
- if (cpos >= csize) {
- cpos = 0;
- csize = Math.min(endoff - off, CHAR_BUF_SIZE);
- s.getChars(off, off + csize, cbuf, 0);
- }
- if (pos >= MAX_BLOCK_SIZE) {
- drain();
- }
- int n = Math.min(csize - cpos, MAX_BLOCK_SIZE - pos);
- int stop = pos + n;
- while (pos < stop) {
- buf[pos++] = (byte) cbuf[cpos++];
- }
- off += n;
- }
- }
- public void writeChars(String s) throws IOException {
- int endoff = s.length();
- for (int off = 0; off < endoff; ) {
- int csize = Math.min(endoff - off, CHAR_BUF_SIZE);
- s.getChars(off, off + csize, cbuf, 0);
- writeChars(cbuf, 0, csize);
- off += csize;
- }
- }
- public void writeUTF(String s) throws IOException {
- writeUTF(s, getUTFLength(s));
- }
String写入的成员函数主要用于写入一个String对象,在写入String的过程中,同样可以使用三种方式写入String对象到一个字节流:字节方式、字符方式、UTF字符串方式;
-----------------额外定义的批量写入-------------
——基础类型数组写入
这种方式可批量写入基础类型的数据,一般其传入参数为一个数组对象:
- void writeBooleans(boolean[] v, int off, int len) throws IOException {
- int endoff = off + len;
- while (off < endoff) {
- if (pos >= MAX_BLOCK_SIZE) {
- drain();
- }
- int stop = Math.min(endoff, off + (MAX_BLOCK_SIZE - pos));
- while (off < stop) {
- Bits.putBoolean(buf, pos++, v[off++]);
- }
- }
- }
- void writeChars(char[] v, int off, int len) throws IOException {
- int limit = MAX_BLOCK_SIZE - 2;
- int endoff = off + len;
- while (off < endoff) {
- if (pos <= limit) {
- int avail = (MAX_BLOCK_SIZE - pos) >> 1;
- int stop = Math.min(endoff, off + avail);
- while (off < stop) {
- Bits.putChar(buf, pos, v[off++]);
- pos += 2;
- }
- } else {
- dout.writeChar(v[off++]);
- }
- }
- }
- void writeShorts(short[] v, int off, int len) throws IOException {
- int limit = MAX_BLOCK_SIZE - 2;
- int endoff = off + len;
- while (off < endoff) {
- if (pos <= limit) {
- int avail = (MAX_BLOCK_SIZE - pos) >> 1;
- int stop = Math.min(endoff, off + avail);
- while (off < stop) {
- Bits.putShort(buf, pos, v[off++]);
- pos += 2;
- }
- } else {
- dout.writeShort(v[off++]);
- }
- }
- }
- void writeInts(int[] v, int off, int len) throws IOException {
- int limit = MAX_BLOCK_SIZE - 4;
- int endoff = off + len;
- while (off < endoff) {
- if (pos <= limit) {
- int avail = (MAX_BLOCK_SIZE - pos) >> 2;
- int stop = Math.min(endoff, off + avail);
- while (off < stop) {
- Bits.putInt(buf, pos, v[off++]);
- pos += 4;
- }
- } else {
- dout.writeInt(v[off++]);
- }
- }
- }
- void writeFloats(float[] v, int off, int len) throws IOException {
- int limit = MAX_BLOCK_SIZE - 4;
- int endoff = off + len;
- while (off < endoff) {
- if (pos <= limit) {
- int avail = (MAX_BLOCK_SIZE - pos) >> 2;
- int chunklen = Math.min(endoff - off, avail);
- floatsToBytes(v, off, buf, pos, chunklen);
- off += chunklen;
- pos += chunklen << 2;
- } else {
- dout.writeFloat(v[off++]);
- }
- }
- }
- void writeLongs(long[] v, int off, int len) throws IOException {
- int limit = MAX_BLOCK_SIZE - 8;
- int endoff = off + len;
- while (off < endoff) {
- if (pos <= limit) {
- int avail = (MAX_BLOCK_SIZE - pos) >> 3;
- int stop = Math.min(endoff, off + avail);
- while (off < stop) {
- Bits.putLong(buf, pos, v[off++]);
- pos += 8;
- }
- } else {
- dout.writeLong(v[off++]);
- }
- }
- }
- void writeDoubles(double[] v, int off, int len) throws IOException {
- int limit = MAX_BLOCK_SIZE - 8;
- int endoff = off + len;
- while (off < endoff) {
- if (pos <= limit) {
- int avail = (MAX_BLOCK_SIZE - pos) >> 3;
- int chunklen = Math.min(endoff - off, avail);
- doublesToBytes(v, off, buf, pos, chunklen);
- off += chunklen;
- pos += chunklen << 3;
- } else {
- dout.writeDouble(v[off++]);
- }
- }
- }
其中批量写入的方式和基础数据写入只有一个区别,因为前面的write方法本身就支持字节数组的写入,所以批量写入的方法定义中没有writeBytes方法。
-----------------String类型的辅助处理-------------
这些辅助方法后边有需要再来详解,目前先提供其代码定义:
- long getUTFLength(String s) {
- int len = s.length();
- long utflen = 0;
- for (int off = 0; off < len; ) {
- int csize = Math.min(len - off, CHAR_BUF_SIZE);
- s.getChars(off, off + csize, cbuf, 0);
- for (int cpos = 0; cpos < csize; cpos++) {
- char c = cbuf[cpos];
- if (c >= 0x0001 && c <= 0x007F) {
- utflen++;
- } else if (c > 0x07FF) {
- utflen += 3;
- } else {
- utflen += 2;
- }
- }
- off += csize;
- }
- return utflen;
- }
- void writeUTF(String s, long utflen) throws IOException {
- if (utflen > 0xFFFFL) {
- throw new UTFDataFormatException();
- }
- writeShort((int) utflen);
- if (utflen == (long) s.length()) {
- writeBytes(s);
- } else {
- writeUTFBody(s);
- }
- }
- void writeLongUTF(String s) throws IOException {
- writeLongUTF(s, getUTFLength(s));
- }
- void writeLongUTF(String s, long utflen) throws IOException {
- writeLong(utflen);
- if (utflen == (long) s.length()) {
- writeBytes(s);
- } else {
- writeUTFBody(s);
- }
- }
- private void writeUTFBody(String s) throws IOException {
- int limit = MAX_BLOCK_SIZE - 3;
- int len = s.length();
- for (int off = 0; off < len; ) {
- int csize = Math.min(len - off, CHAR_BUF_SIZE);
- s.getChars(off, off + csize, cbuf, 0);
- for (int cpos = 0; cpos < csize; cpos++) {
- char c = cbuf[cpos];
- if (pos <= limit) {
- if (c <= 0x007F && c != 0) {
- buf[pos++] = (byte) c;
- } else if (c > 0x07FF) {
- buf[pos + 2] = (byte) (0x80 | ((c >> 0) & 0x3F));
- buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F));
- buf[pos + 0] = (byte) (0xE0 | ((c >> 12) & 0x0F));
- pos += 3;
- } else {
- buf[pos + 1] = (byte) (0x80 | ((c >> 0) & 0x3F));
- buf[pos + 0] = (byte) (0xC0 | ((c >> 6) & 0x1F));
- pos += 2;
- }
- } else { // write one byte at a time to normalize block
- if (c <= 0x007F && c != 0) {
- write(c);
- } else if (c > 0x07FF) {
- write(0xE0 | ((c >> 12) & 0x0F));
- write(0x80 | ((c >> 6) & 0x3F));
- write(0x80 | ((c >> 0) & 0x3F));
- } else {
- write(0xC0 | ((c >> 6) & 0x1F));
- write(0x80 | ((c >> 0) & 0x3F));
- }
- }
- }
- off += csize;
- }
- }
-----------------输出流的辅助处理-------------
实际上从上边的方法定义中可以知道,该类中的大部分方法主要是用于基础类型数据以及String的写入操作,而整个输出流中有4个辅助方法,这四个辅助方法的作用如下:
- flush
该方法用于将缓冲区中的数据写入到目标介质,一般的Java输出流对象中都包含了这个方法; - close
该方法用于关闭当前输出流; - drain
该方法将当前输出流中所有缓冲的数据写入到underlying stream中,但是它不去flush“underlying stream”中的数据; - writeBlockHeader
该方法用于写入Data Block的头信息,针对普通的Data Block以及大数据块的Data Block分别写入;
- public void flush() throws IOException {
- drain();
- out.flush();
- }
- public void close() throws IOException {
- flush();
- out.close();
- }
- void drain() throws IOException {
- if (pos == 0) {
- return;
- }
- if (blkmode) {
- writeBlockHeader(pos);
- }
- out.write(buf, 0, pos);
- pos = 0;
- }
- private void writeBlockHeader(int len) throws IOException {
- if (len <= 0xFF) {
- hbuf[0] = TC_BLOCKDATA;
- hbuf[1] = (byte) len;
- out.write(hbuf, 0, 2);
- } else {
- hbuf[0] = TC_BLOCKDATALONG;
- Bits.putInt(hbuf, 1, len);
- out.write(hbuf, 0, 5);
- }
- }
到这里所有ObjectOutputStream中的内部类就已经解析完了,接下来看看主类中的信息。
3)成员属性
从前文可以知道,ObjectOutputStream的内部类主要是定义了常用的序列化方法【write*】、需要使用的数据结构【哈希表】以及调试辅助工具,虽然前文并没有解析部分方法的细节内容,但读者可以从其代码实现中自行阅读并且加以理解,在本文后边提供示例的时候,我们会通过代码执行流程来分析序列化的基本步骤,那个时候我会尽可能把前边涉及的内容做一个整合分析。这里我们先来看看ObjectOutputStream类中的成员属性信息:
-----------------哈希表和输出流-------------
- /** filter stream for handling block data conversion */
- private final BlockDataOutputStream bout;
- /** obj -> wire handle map */
- private final HandleTable handles;
- /** obj -> replacement obj map */
- private final ReplaceTable subs;
- bout——java.io.ObjectOutputStream.BlockDataOutputStream
该成员为ObjectOutputStream中的“潜在输出流”,它则以Data Block的方式写入数据到字节流; - handler——java.io.ObjectOutputStream.HandleTable
该成员为一个哈希表,它表示从对象到引用的映射;
- subs——java.io.ObjectOutputStream.RepalceTable
该成员为一个哈希表,它表示从对象到“替换对象”的一个映射关系;
- /** stream protocol version */
- private int protocol = PROTOCOL_VERSION_2;
- /** recursion depth */
- private int depth;
- /** buffer for writing primitive field values */
- private byte[] primVals;
- protocol——int
当前序列化使用的字节流协议的版本号; - depth——int
需要递归的深度,这个属性主要用于有多个父类的情况,如果出现了由上至下或由下至上的递归的时候使用; - primVals——byte[]
当前对象中基础类型的字段的值数据存储的字节数组;
- /** if true, invoke writeObjectOverride() instead of writeObject() */
- private final boolean enableOverride;
- /** if true, invoke replaceObject() */
- private boolean enableReplace;
- // values below valid only during upcalls to writeObject()/writeExternal()
- /**
- * Context during upcalls to class-defined writeObject methods; holds
- * object currently being serialized and descriptor for current class.
- * Null when not during writeObject upcall.
- */
- private SerialCallbackContext curContext;
- /** current PutField object */
- private PutFieldImpl curPut;
- /** custom storage for debug trace info */
- private final DebugTraceInfoStack debugInfoStack;
- /**
- * value of "sun.io.serialization.extendedDebugInfo" property,
- * as true or false for extended information about exception's place
- */
- private static final boolean extendedDebugInfo =
- java.security.AccessController.doPrivileged(
- new sun.security.action.GetBooleanAction(
- "sun.io.serialization.extendedDebugInfo")).booleanValue();
- enableOverride——boolean
如果为true,在序列化Java对象时使用writeObjectOverride方法代替writeObject方法; - enableReplace——boolean
如果为true,调用replaceObject方法,否则不调用; - curContext——java.io.SerialCallbackContext
该成员属性为序列化的回调设置提供了上下文环境,如果Java对象重写了writeObject方法,则使用该成员属性判断调用此方法的上下文环境,在JVM序列化的规范中提到过重写的部分方法必须在ObjectOutputStream内部调用,若在其他地方调用则会抛出NotActiveException异常,该成员属性则用于指向这些方法调用的上下文环境。 - curPut——java.io.ObjectOutputStream.PutFieldImpl
该成员属性为ObjectOutputStream内部的一个默认序列化字段时的实现实例,PutFieldImpl前文讲过,这里不重复; - debugInfoStack——java.io.ObjectOutputStream.DebugTraceInfoStack
成成员属性用于辅助调试,可自定义Debug过程中堆栈信息的存储细节等; - extendedDebugInfo——boolean
JVM环境中属性sun.io.serialization.extendedDebugInfo的值,该值用于启用/禁用Debug功能,这个值在定义和赋值过程调用了Java的访问控制器来检查代码的执行权限;
- /**
- * Converts specified span of float values into byte values.
- */
- // REMIND: remove once hotspot inlines Float.floatToIntBits
- private static native void floatsToBytes(float[] src, int srcpos,
- byte[] dst, int dstpos,
- int nfloats);
- /**
- * Converts specified span of double values into byte values.
- */
- // REMIND: remove once hotspot inlines Double.doubleToLongBits
- private static native void doublesToBytes(double[] src, int srcpos,
- byte[] dst, int dstpos,
- int ndoubles);
- public void write(int val) throws IOException {
- bout.write(val);
- }
- public void write(byte[] buf) throws IOException {
- bout.write(buf, 0, buf.length, false);
- }
- public void write(byte[] buf, int off, int len) throws IOException {
- if (buf == null) {
- throw new NullPointerException();
- }
- int endoff = off + len;
- if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) {
- throw new IndexOutOfBoundsException();
- }
- bout.write(buf, off, len, false);
- }
- public void writeBoolean(boolean val) throws IOException {
- bout.writeBoolean(val);
- }
- public void writeByte(int val) throws IOException {
- bout.writeByte(val);
- }
- public void writeShort(int val) throws IOException {
- bout.writeShort(val);
- }
- public void writeChar(int val) throws IOException {
- bout.writeChar(val);
- }
- public void writeInt(int val) throws IOException {
- bout.writeInt(val);
- }
- public void writeLong(long val) throws IOException {
- bout.writeLong(val);
- }
- public void writeFloat(float val) throws IOException {
- bout.writeFloat(val);
- }
- public void writeDouble(double val) throws IOException {
- bout.writeDouble(val);
- }
- public void writeBytes(String str) throws IOException {
- bout.writeBytes(str);
- }
- public void writeChars(String str) throws IOException {
- bout.writeChars(str);
- }
- public void writeUTF(String str) throws IOException {
- bout.writeUTF(str);
- }
- public void flush() throws IOException {
- bout.flush();
- }
- protected void drain() throws IOException {
- bout.drain();
- }
- public void close() throws IOException {
- flush();
- clear();
- bout.close();
- }
- public void reset() throws IOException {
- if (depth != 0) {
- throw new IOException("stream active");
- }
- bout.setBlockDataMode(false);
- bout.writeByte(TC_RESET);
- clear();
- bout.setBlockDataMode(true);
- }
- flush
该成员函数负责将缓冲区中的数据写入到字节流并且刷新缓冲区; - close
流关闭方法,该方法会调用flush函数,并且调用clear函数【后边说明】清空两个哈希表,最后调用bout的close函数关闭underlying的潜在输出流; - drain
该方法直接调用bout的drain方法,其作用和BlockDataOutputStream中的drain方法一样; - reset
该方法的调用将会放弃所有写入到字节流中的对象的状态信息,并且将字节流重置到一个新的ObjectOutputStream对象中,当前的字节流中的偏移量也会自动重置;在写入TC_RESET标记时,会先关闭Data Block模式,等到重置信息写入过后,再将Data Block模式打开;不仅仅如此,如果depth成员属性不为0时,会抛出IOException异常信息;
- public void useProtocolVersion(int version) throws IOException {
- if (handles.size() != 0) {
- // REMIND: implement better check for pristine stream?
- throw new IllegalStateException("stream non-empty");
- }
- switch (version) {
- case PROTOCOL_VERSION_1:
- case PROTOCOL_VERSION_2:
- protocol = version;
- break;
- default:
- throw new IllegalArgumentException(
- "unknown version: " + version);
- }
- }
- int getProtocolVersion() {
- return protocol;
- }
- useProtocolVersion
设置当前对象序列化时的流协议版本信息,注意版本信息的设置过程——如果handles中有值时,会抛出IllegalStateException异常信息,也就是说如果要设置流协议版本必须保证当前的字节流中没有任何数据。在判断传入的version版本中,只支持值为PROTOCOL_VERSION_1和PROTOCOL_VERSION_2两个值,若传入的version值不匹配序列化流协议值,同样抛出IllegalArgumentException异常信息; - getProtocolVersion
获取当前对象序列化时的流协议版本信息;
- public ObjectOutputStream.PutField putFields() throws IOException {
- if (curPut == null) {
- if (curContext == null) {
- throw new NotActiveException("not in call to writeObject");
- }
- Object curObj = curContext.getObj();
- ObjectStreamClass curDesc = curContext.getDesc();
- curPut = new PutFieldImpl(curDesc);
- }
- return curPut;
- }
- public void writeFields() throws IOException {
- if (curPut == null) {
- throw new NotActiveException("no current PutField object");
- }
- bout.setBlockDataMode(false);
- curPut.writeFields();
- bout.setBlockDataMode(true);
- }
- putFields
该方法调用过后会返回ObjectOutputStream.PutField对象,执行的时候它会先检查该方法的调用环境,即成员属性curContext的值,如果该值为null则表示调用此方法的上下文环境不对——putFields方法只能在ObjectOutputStream中的writeObject调用,不能在其他位置调用该方法。最后该方法从curContext获取对象和类元数据信息,然后初始化curPut成员属性; -
writeFields该方法在调用时必须保证curPut成员属性不为null,即上下文环境中已经存在curPut的情况下才能调用该方法,这个方法主要会调用PutFieldImpl中的writeFields方法。在调用writeFields之前会先关闭输出流的Data Block模式,调用之后再开启Data Block模式;
- private void writeFatalException(IOException ex) throws IOException {
- clear();
- boolean oldMode = bout.setBlockDataMode(false);
- try {
- bout.writeByte(TC_EXCEPTION);
- writeObject0(ex, false);
- clear();
- } finally {
- bout.setBlockDataMode(oldMode);
- }
- }
- private void writeNull() throws IOException {
- bout.writeByte(TC_NULL);
- }
- private void writeHandle(int handle) throws IOException {
- bout.writeByte(TC_REFERENCE);
- bout.writeInt(baseWireHandle + handle);
- }
- private void writeClass(Class cl, boolean unshared) throws IOException {
- bout.writeByte(TC_CLASS);
- writeClassDesc(ObjectStreamClass.lookup(cl, true), false);
- handles.assign(unshared ? null : cl);
- }
- private void writeProxyDesc(ObjectStreamClass desc, boolean unshared)
- throws IOException
- {
- bout.writeByte(TC_PROXYCLASSDESC);
- handles.assign(unshared ? null : desc);
- Class cl = desc.forClass();
- Class[] ifaces = cl.getInterfaces();
- bout.writeInt(ifaces.length);
- for (int i = 0; i < ifaces.length; i++) {
- bout.writeUTF(ifaces[i].getName());
- }
- bout.setBlockDataMode(true);
- annotateProxyClass(cl);
- bout.setBlockDataMode(false);
- bout.writeByte(TC_ENDBLOCKDATA);
- writeClassDesc(desc.getSuperDesc(), false);
- }
- 先写入TC_PROXYCLASSDESC标记信息;
- 如果使用的模式是非共享模式,则需要将desc所表示的类元数据信息插入到引用->对象的映射表中【*:插入位置是下一个位置,这里调用的是assign方法,而不是insert方法。】;
- 再写入当前对象所属类的接口信息:先写入接口数量,其次遍历所有接口写入每一个接口名称;
- 然后需要调用annotateProxyClass方法,在调用该方法之前开启Data Block模式,调用完成过后再关闭Data Block模式;
- 之后再写入TC_ENDBLOCKDATA标记作为当前动态代理类的描述信息的结束;
- 最后再调用writeClassDesc方法去写入当前对象所属类的父类元数据信息,写入时不以“unshared”方式写入;
- private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared)
- throws IOException
- {
- bout.writeByte(TC_CLASSDESC);
- handles.assign(unshared ? null : desc);
- if (protocol == PROTOCOL_VERSION_1) {
- // do not invoke class descriptor write hook with old protocol
- desc.writeNonProxy(this);
- } else {
- writeClassDescriptor(desc);
- }
- Class cl = desc.forClass();
- bout.setBlockDataMode(true);
- annotateClass(cl);
- bout.setBlockDataMode(false);
- bout.writeByte(TC_ENDBLOCKDATA);
- writeClassDesc(desc.getSuperDesc(), false);
- }
- 先写入TC_CLASSDESC标记信息;
- 如果使用的模式是非共享模式,则将desc所表示的类元数据信息插入到引用->对象的映射表中;
- 然后根据使用的流协议版本调用不同的write方法:
a.如果使用的流协议是PROTOCOL_VERSION_1,则直接调用desc成员的writeNonProxy方法,将当前引用this作为实参传入到writeNonProxy方法;
b.如果使用的流协议不是PROTOCOL_VERSION_1,则调用当前类中的writeClassDescriptor方法; - 然会需要调用annotateClass方法,在调用该方法之前开启Data Block模式,调用完成过后再关闭Data Block模式;
- 之后再写入TC_ENDBLOCKDATA标记作为当前非动态代理类的描述信息的结束;
- 最后调用writeClassDesc方法写入当前对象所属类的父类元数据信息,写入时同样不使用“unshared”方式写入;
- private void writeString(String str, boolean unshared) throws IOException {
- handles.assign(unshared ? null : str);
- long utflen = bout.getUTFLength(str);
- if (utflen <= 0xFFFF) {
- bout.writeByte(TC_STRING);
- bout.writeUTF(str, utflen);
- } else {
- bout.writeByte(TC_LONGSTRING);
- bout.writeLongUTF(str, utflen);
- }
- }
- 写入String对象之前,系统同样会判断当前写入方式是否是“unshared”,如果不是“unshared”方式则需要在引用->对象映射中插入当前String对象;
- 其次调用getUTFLength函数获取String字符串的长度,获取该长度时使用UTF-8编码方式获取;
- 使用getUTFLength方法的返回值和0xFFFF比较:
如果大于该值表示当前String对象是一个长字符串对象——先写入TC_LONGSTRING标记,然后写入字符串的长度和内容;
如果小于等于该值表示当前String对象就是一个普通的字符串对象——先写入TC_STRING标记,然后写入字符串的长度和内容;
- private void writeArray(Object array,
- ObjectStreamClass desc,
- boolean unshared)
- throws IOException
- {
- bout.writeByte(TC_ARRAY);
- writeClassDesc(desc, false);
- handles.assign(unshared ? null : array);
- 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();
- }
- }
- }
- }
- 先写入TC_ARRAY标记信息;
- 然后写入这个数组的类描述信息,写入方式使用“unshared”方式;
- 如果使用的模式是非共享模式,则将desc所表示的类元数据信息插入到引用->对象的映射表中;
- 随后获取当前数组中元素的类型:
a.如果元素是基础类型——先写入该数组的长度,其次写入这些数组中所有元素的值;
b.如果元素是对象类型——先写入该数组的长度,其次调用writeObject0方法写入这些数组中每一个对象元素;
- private void writeEnum(Enum en,
- ObjectStreamClass desc,
- boolean unshared)
- throws IOException
- {
- bout.writeByte(TC_ENUM);
- ObjectStreamClass sdesc = desc.getSuperDesc();
- writeClassDesc((sdesc.forClass() == Enum.class) ? desc : sdesc, false);
- handles.assign(unshared ? null : en);
- writeString(en.name(), false);
- }
- 先写入TC_ENUM标记信息;
- 然后获取当前类的父类元数据信息判断是否枚举类信息,如果是枚举类则写入枚举类信息,否则写入当前类的父类信息——从JDK 1.5引入了枚举类型过后,这些枚举信息都继承于Enum.class,所以判断一个类是否枚举类使用这种方式判断;
- 如果使用的模式是非共享模式,则将desc所表示的类元数据信息插入到引用->对象的映射表中;
- 最后将调用枚举类型中的name()方法,将枚举类型的字符串字面量以String方法写入字节流;
- 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();
- }
- }
- }
- 在写入Java对象信息之前,需要先调用ObjectStreamClass检查当前对象是否是一个可序列化对象;
- 其次写入TC_OBJECT标记;
- 随后调用writeClassDesc方法写入当前对象所属类的类描述信息;
- 如果使用的模式是非共享模式,则将desc所表示的类元数据信息插入到引用->对象的映射表中;
- 随后判断当前Java对象的序列化语义:
a.如果当前对象不是一个动态代理类并且是实现了外部化的,则调用writeExternalData方法写入对象信息;
b.如果当前对象是一个实现了Serializable接口的,则调用writeSerialData方法写入对象信息;
- private void writeExternalData(Externalizable obj) throws IOException {
- PutFieldImpl oldPut = curPut;
- curPut = null;
- if (extendedDebugInfo) {
- debugInfoStack.push("writeExternal data");
- }
- SerialCallbackContext oldContext = curContext;
- 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);
- }
- } finally {
- curContext = oldContext;
- if (extendedDebugInfo) {
- debugInfoStack.pop();
- }
- }
- curPut = oldPut;
- }
- 该方法会判断目前使用的字节流协议:
a.如果使用的是PROTOCOL_VERSION_1协议,则直接调用可序列化对象中的writeExternal方法;
b.如果不是使用的PROTOCOL_VERSION_1协议,则先开启Data Block模式,再调用writeExternal方法,调用过后关闭Data Block模式,并且在最后追加TC_ENDBLOCKDATA标记; - 注意这个方法有一个切换上下文环境的过程,定义了一个新的SerialCallbackContext类型的引用oldContext用于保存执行之前的环境,在finally块中将当前环境复原——也就是说在调用writeExternal方法时,curContext的值为null;
- 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 {
- defaultWriteFields(obj, slotDesc);
- }
- }
- }
- 在序列化当前对象之前,先从类描述信息中获取ClassDataSlot信息;
- 判断可序列化对象是否重写了writeObject方法:
a.如果没有重写该方法,则调用defaultWriteFields方法写入当前对象中的所有字段信息;
b.如果重写了该方法,则先开启Data Block模式,再调用writeObject方法,调用过后关闭Data Block模式,并且在最后追加TC_ENDBLOCKDATA标记; - 注该方法执行过程同样有一个切换上下文环境的过程,读者自诩阅读和curContext相关的代码行就可以理解了;
- public void defaultWriteObject() throws IOException {
- if ( curContext == null ) {
- throw new NotActiveException("not in call to writeObject");
- }
- Object curObj = curContext.getObj();
- ObjectStreamClass curDesc = curContext.getDesc();
- bout.setBlockDataMode(false);
- defaultWriteFields(curObj, curDesc);
- bout.setBlockDataMode(true);
- }
- 这个方法只能在writeObject方法内调用,如果检查curContext环境发现当前方法的调用不是在writeObject方法中,则抛出NotActiveException异常信息;
- 从上下文环境中获取对象和类描述信息;
- 最后关闭Data Block模式,调用defaultWriteFields方法将字段信息写入到字节流,调用之后再开启Data Block模式;
- private void defaultWriteFields(Object obj, ObjectStreamClass desc)
- throws IOException
- {
- // REMIND: perform conservative isInstance check here?
- 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();
- }
- }
- }
- }
- 该方法会先检查可序列化的语义;
- 然后获取该对象中所有基础类型字段的值,获得过后写入这些基础类型数据到字节流;
- 完成基础类型的字段值写入过程,再调用writeObject0方法写入对象类型的字段的值;
- protected void writeClassDescriptor(ObjectStreamClass desc)
- throws IOException
- {
- desc.writeNonProxy(this);
- }
- void writeTypeString(String str) throws IOException {
- int handle;
- if (str == null) {
- writeNull();
- } else if ((handle = handles.lookup(str)) != -1) {
- writeHandle(handle);
- } else {
- writeString(str, false);
- }
- }
- 如果传入的String引用是一个null引用,则调用writeNull方法;
- 如果能够在字符串常量池中找到传入的String对象,则调用writeHandle方法;
- 上边两个条件都不满足时,直接调用writeString使用“unshared”方式写入传入的String对象;
- private void writeClassDesc(ObjectStreamClass desc, boolean unshared)
- throws IOException
- {
- int handle;
- if (desc == null) {
- writeNull();
- } else if (!unshared && (handle = handles.lookup(desc)) != -1) {
- writeHandle(handle);
- } else if (desc.isProxy()) {
- writeProxyDesc(desc, unshared);
- } else {
- writeNonProxyDesc(desc, unshared);
- }
- }
- 如果传入的类描述信息是一个null引用,则调用writeNull方法;
- 如果使用了非“unshared”方式,并且可以在对象池中找到传入的对象信息,则调用writeHandle;
- 如果传入的类是一个动态代理类,则调用writeProxyDesc方法;
- 以上条件都不满足时,则调用writeNonProxyDesc方法;
- 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;
- }
- }
- protected void writeObjectOverride(Object obj) throws IOException {
- }
- 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 object
- Object 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
- 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());
- }
- }
- } finally {
- depth--;
- bout.setBlockDataMode(oldMode);
- }
- }
- 先关闭输出流的Data Block模式,并且将原始模式赋值给变量oldMode;
- 如果当前传入对象在“替换哈希表【ReplaceTable】”中无法找到,则调用writeNull方法并且返回;
如果当前写入方式是非“unshared”方式,并且可以在“引用哈希表【HandleTable】”中找到该引用,则调用writeHandle方法并且返回;
如果当前传入对象是特殊类型Class类型,则调用writeClass方法并且返回;
如果当前传入对象是特殊类型ObjectStreamClass,则调用writeClassDesc方法并且返回; - 以上条件都不满足时【传入对象不是ObjectStreamClass和Class类型,而且使用了“unshared”方式,“替换哈希表”中没有该记录】,需要检查替换对象,在检查替换对象时需要跳过string/arrays类型,通过循环方式查找“替换对象”;
【*:到这里可以理解Java序列化中的替换对象是什么概念了——前文JVM的序列化规范中提到过“替换”对象的概念,实际上替换对象就是在可序列化方法中重写了writeReplace方法的Java对象。】 - 通过检查成员属性enableReplace的值判断当前对象是否启用了“替换”功能【Replace功能】;
- 对象替换过后,则需要对原始对象进行二次检查,先将替换对象插入到“替换哈希表”中,然后执行和第二步一模一样的检查来检查原始对象;
- 以上执行都完成过后,处理剩余对象类型:
如果传入对象为String类型,调用writeString方法将数据写入字节流;
如果传入对象为Array类型,调用writeArray方法将数据写入字节流;
如果传入对象为Enum类型,调用writeEnum方法将数据写入字节流;
如果传入对象实现了Serializable接口,调用writeOrdinaryObject方法将数据写入字节流;
以上条件都不满足时则抛出NotSerializableException异常信息; - 执行完上述代码过后,将输出流的Data Block模式还原;
- protected void writeStreamHeader() throws IOException {
- bout.writeShort(STREAM_MAGIC);
- bout.writeShort(STREAM_VERSION);
- }
- public void writeUnshared(Object obj) throws IOException {
- try {
- writeObject0(obj, true);
- } catch (IOException ex) {
- if (depth == 0) {
- writeFatalException(ex);
- }
- throw ex;
- }
- }
- protected void annotateClass(Class<?> cl) throws IOException {
- }
- protected void annotateProxyClass(Class<?> cl) throws IOException {
- }
- protected Object replaceObject(Object obj) throws IOException {
- return obj;
- }
- annotateClass
子类可以实现该方法,这样将允许类中的数据存储在字节流中,默认情况下这个方法什么也不做,和该方法对应的ObjectInputStream中的方法为resolveClass。这个方法对于字节流中的类只会调用唯一的一次,类名以及类签名信息都会写入到字节流中,这个方法会使用ObjectOutputStream类自由写入任何格式的类信息,例如默认的字节文件。不仅仅如此,ObjectInputStream类中的resolveClass方法会读取被annotateClass方法写入的任何基础数据和对象数据; - annotateProxyClass
子类可以实现该方法用于定制字节流中用于描述动态代理类的数据信息;这个方法针对每一个动态代理方法只会调用唯一的一次,它的默认实现却什么也不做; - replaceObject
前文多次提到“替换对象”——“替换对象”实际上就是重写了该方法的对象。这个方法允许ObjectOutputStream的可信任子类在序列化过程中替换另外一个对象,默认情况下“替换对象”功能是未开启的,一旦调用了enableReplaceObject方法后就启用了序列化中的“替换对象”功能。enableReplaceObject方法会检查字节流中的替换请求是否可信任的,序列化字节流中第一个写入的可匹配对象将会传给replaceObject方法,接下来写入字节流的对象的引用也会被原始对象调用replaceObject方法替换掉。必须要确保的是对象的私有状态不会暴露在外,只有可信任的字节流可调用replaceObject方法;
a.ObjectOutputStream的writeObject方法会使用一个Object【实现了Serializable接口】作为参数,它允许不可序列化对象被可序列化对象替换;
b.当一个子类是一个替换对象,它必须保证序列化替换中的必须替换操作或者这些可替换对象的每一个字段和引用指向的原始对象是兼容的。如果对象类型并不是字段的子类或者数组元素子类,则中断序列化过程抛出异常,这种情况下不会保存该对象;
c.这个方法仅仅在第一次遇到同类型对象时调用,所有之后的引用将重定向到新的对象中,这个方法将在执行过后返回原始对象或者被替换的新对象;
d.null可以作为一个对象被替换,但是它有可能引起NullReferenceException异常;
- protected boolean enableReplaceObject(boolean enable)
- throws SecurityException
- {
- if (enable == enableReplace) {
- return enable;
- }
- if (enable) {
- SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- sm.checkPermission(SUBSTITUTION_PERMISSION);
- }
- }
- enableReplace = enable;
- return !enableReplace;
- }
- 如果该功能已经启用,并且传入参数为true,则什么都不做直接返回;
- 如果想要启用“替换”功能,则需要调用Java安全管理器,并且检查权限SUBSTITUTION_PERMISSION;
- 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;
- }
- }
- protected ObjectOutputStream() throws IOException, SecurityException {
- SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
- }
- bout = null;
- handles = null;
- subs = null;
- enableOverride = true;
- debugInfoStack = null;
- }
- 调用安全管理器SecurityManager,并且使用安全管理器检查权限SUBCLASS_IMPLEMENTATION_PERMISSION;
- 设置成员属性的默认值:
bout——null、handles——null、subs——null、enableOverride——true、debugInfoStack——null
- 调用verifySubclass方法验证子类信息;
- 初始化bout成员属性,实例化一个BlockDataOutputStream;
- 初始化handles和subs,创建两个对应的实例;
- 将enableOverride成员属性的值设置成false;
- 调用writeStreamHeader方法写入魔数和序列化版本信息;
- 开启Data Block模式写入信息;
- 如果启用了调试模式,则需要实例化debugInfoStack;
- private void verifySubclass() {
- Class cl = getClass();
- if (cl == ObjectOutputStream.class) {
- return;
- }
- SecurityManager sm = System.getSecurityManager();
- if (sm == null) {
- return;
- }
- processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits);
- WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue);
- Boolean result = Caches.subclassAudits.get(key);
- if (result == null) {
- result = Boolean.valueOf(auditSubclass(cl));
- Caches.subclassAudits.putIfAbsent(key, result);
- }
- if (result.booleanValue()) {
- return;
- }
- sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
- }
- private static boolean auditSubclass(final Class subcl) {
- Boolean result = AccessController.doPrivileged(
- new PrivilegedAction<Boolean>() {
- public Boolean run() {
- for (Class cl = subcl;
- cl != ObjectOutputStream.class;
- cl = cl.getSuperclass())
- {
- try {
- cl.getDeclaredMethod(
- "writeUnshared", new Class[] { Object.class });
- return Boolean.FALSE;
- } catch (NoSuchMethodException ex) {
- }
- try {
- cl.getDeclaredMethod("putFields", (Class[]) null);
- return Boolean.FALSE;
- } catch (NoSuchMethodException ex) {
- }
- }
- return Boolean.TRUE;
- }
- }
- );
- return result.booleanValue();
- }
- private void clear() {
- subs.clear();
- handles.clear();
- }
- public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamConstants
- InputStream是一个抽象类,它表示字节输入流的所有类的超类,需要定义InputStream子类的应用程序必须总是提供返回下一个输入字节的方法;
- ObjectInput接口扩展DataInput接口包含对象的操作,DataInput接口包括了基本类型的输入方法,ObjectInput扩展了该接口,以包含对象、数组和String的输入方法;
- private static class Caches
- public static abstract class GetField
- private class GetFieldImpl extends GetField
- private static class ValidationList
- private static class PeekInputStream extends InputStream
- private class BlockDataInputStream extends InputStream implements DataInput
- private static class HandleTable
- /** end offset of valid data in buf, or -1 if no more block data */
- private int end = -1;
- /** number of bytes in current block yet to be read from stream */
- private int unread = 0;
- end
表示读取某一段Data Block时该数据块的结束偏移量,如果该数据块中没有合法数据,则返回-1; - unread
用来统计当前Data Block中未读取的字节的数量;
- int currentBlockRemaining() {
- if (blkmode) {
- return (end >= 0) ? (end - pos) + unread : 0;
- } else {
- throw new IllegalStateException();
- }
- }
- void skipBlockData() throws IOException {
- if (!blkmode) {
- throw new IllegalStateException("not in block data mode");
- }
- while (end >= 0) {
- refill();
- }
- }
- public long skip(long len) throws IOException {
- long remain = len;
- while (remain > 0) {
- if (blkmode) {
- if (pos == end) {
- refill();
- }
- if (end < 0) {
- break;
- }
- int nread = (int) Math.min(remain, end - pos);
- remain -= nread;
- pos += nread;
- } else {
- int nread = (int) Math.min(remain, MAX_BLOCK_SIZE);
- if ((nread = in.read(buf, 0, nread)) < 0) {
- break;
- }
- remain -= nread;
- }
- }
- return len - remain;
- }
- private static final byte STATUS_OK = 1;
- private static final byte STATUS_UNKNOWN = 2;
- private static final byte STATUS_EXCEPTION = 3;
- STATUS_OK
表示当前读取的数据是合法数据; - STATUS_UNKNOWN
表示当前读取的对象数据为“Unknown”对象,即无法识别; - STATUS_EXCEPTION
表示当前读取数据的过程出现了异常信息
- void markDependency(int dependent, int target) {
- if (dependent == NULL_HANDLE || target == NULL_HANDLE) {
- return;
- }
- switch (status[dependent]) {
- case STATUS_UNKNOWN:
- switch (status[target]) {
- case STATUS_OK:
- // ignore dependencies on objs with no exception
- break;
- case STATUS_EXCEPTION:
- // eagerly propagate exception
- markException(dependent,
- (ClassNotFoundException) entries[target]);
- break;
- case STATUS_UNKNOWN:
- // add to dependency list of target
- if (deps[target] == null) {
- deps[target] = new HandleList();
- }
- deps[target].add(dependent);
- // remember lowest unresolved target seen
- if (lowDep < 0 || lowDep > target) {
- lowDep = target;
- }
- break;
- default:
- throw new InternalError();
- }
- break;
- case STATUS_EXCEPTION:
- break;
- default:
- throw new InternalError();
- }
- }
- void markException(int handle, ClassNotFoundException ex) {
- switch (status[handle]) {
- case STATUS_UNKNOWN:
- status[handle] = STATUS_EXCEPTION;
- entries[handle] = ex;
- // propagate exception to dependents
- HandleList dlist = deps[handle];
- if (dlist != null) {
- int ndeps = dlist.size();
- for (int i = 0; i < ndeps; i++) {
- markException(dlist.get(i), ex);
- }
- deps[handle] = null;
- }
- break;
- case STATUS_EXCEPTION:
- break;
- default:
- throw new InternalError();
- }
- }
- private static class PeekInputStream extends InputStream {
- /** underlying stream */
- private final InputStream in;
- /** peeked byte */
- private int peekb = -1;
- /**
- * Creates new PeekInputStream on top of given underlying stream.
- */
- PeekInputStream(InputStream in) {
- this.in = in;
- }
- /**
- * Peeks at next byte value in stream. Similar to read(), except
- * that it does not consume the read value.
- */
- int peek() throws IOException {
- return (peekb >= 0) ? peekb : (peekb = in.read());
- }
- public int read() throws IOException {
- if (peekb >= 0) {
- int v = peekb;
- peekb = -1;
- return v;
- } else {
- return in.read();
- }
- }
- public int read(byte[] b, int off, int len) throws IOException {
- if (len == 0) {
- return 0;
- } else if (peekb < 0) {
- return in.read(b, off, len);
- } else {
- b[off++] = (byte) peekb;
- len--;
- peekb = -1;
- int n = in.read(b, off, len);
- return (n >= 0) ? (n + 1) : 1;
- }
- }
- void readFully(byte[] b, int off, int len) throws IOException {
- int n = 0;
- while (n < len) {
- int count = read(b, off + n, len - n);
- if (count < 0) {
- throw new EOFException();
- }
- n += count;
- }
- }
- public long skip(long n) throws IOException {
- if (n <= 0) {
- return 0;
- }
- int skipped = 0;
- if (peekb >= 0) {
- peekb = -1;
- skipped++;
- n--;
- }
- return skipped + skip(n);
- }
- public int available() throws IOException {
- return in.available() + ((peekb >= 0) ? 1 : 0);
- }
- public void close() throws IOException {
- in.close();
- }
- }
- private static class ValidationList {
- private static class Callback {
- final ObjectInputValidation obj;
- final int priority;
- Callback next;
- final AccessControlContext acc;
- Callback(ObjectInputValidation obj, int priority, Callback next,
- AccessControlContext acc)
- {
- this.obj = obj;
- this.priority = priority;
- this.next = next;
- this.acc = acc;
- }
- }
- private Callback list;
- ValidationList() {
- }
- void register(ObjectInputValidation obj, int priority)
- throws InvalidObjectException
- {
- if (obj == null) {
- throw new InvalidObjectException("null callback");
- }
- Callback prev = null, cur = list;
- while (cur != null && priority < cur.priority) {
- prev = cur;
- cur = cur.next;
- }
- AccessControlContext acc = AccessController.getContext();
- if (prev != null) {
- prev.next = new Callback(obj, priority, cur, acc);
- } else {
- list = new Callback(obj, priority, list, acc);
- }
- }
- void doCallbacks() throws InvalidObjectException {
- try {
- while (list != null) {
- AccessController.doPrivileged(
- new PrivilegedExceptionAction<Void>()
- {
- public Void run() throws InvalidObjectException {
- list.obj.validateObject();
- return null;
- }
- }, list.acc);
- list = list.next;
- }
- } catch (PrivilegedActionException ex) {
- list = null;
- throw (InvalidObjectException) ex.getException();
- }
- }
- public void clear() {
- list = null;
- }
- }
- /** handle value representing null */
- private static final int NULL_HANDLE = -1;
- /** marker for unshared objects in internal handle table */
- private static final Object unsharedMarker = new Object();
- /** table mapping primitive type names to corresponding class objects */
- private static final HashMap<String, Class<?>> primClasses
- = new HashMap<>(8, 1.0F);
- NULL_HANDLE
该成员表示null值,因为读取过程引用Handle是一个整数,所以用整数中的-1表示null引用; - unsharedMarker
该对象主要用于在内部的“引用哈希表”中标记“unshared”状态的对象——“unshared”状态的对象的重建步骤和非“unshared”不一样; - primClasses
该哈希表用于存储基础类型名称到对应的类实例的映射,它在静态初始化块中会被初始化:
- static {
- primClasses.put("boolean", boolean.class);
- primClasses.put("byte", byte.class);
- primClasses.put("char", char.class);
- primClasses.put("short", short.class);
- primClasses.put("int", int.class);
- primClasses.put("long", long.class);
- primClasses.put("float", float.class);
- primClasses.put("double", double.class);
- primClasses.put("void", void.class);
- }
- /** filter stream for handling block data conversion */
- private final BlockDataInputStream bin;
- /** validation callback list */
- private final ValidationList vlist;
- /** recursion depth */
- private int depth;
- /** whether stream is closed */
- private boolean closed;
- /** wire handle -> obj/exception map */
- private final HandleTable handles;
- /** scratch field for passing handle values up/down call stack */
- private int passHandle = NULL_HANDLE;
- /** flag set when at end of field value block with no TC_ENDBLOCKDATA */
- private boolean defaultDataEnd = false;
- /** buffer for reading primitive field values */
- private byte[] primVals;
- /** if true, invoke readObjectOverride() instead of readObject() */
- private final boolean enableOverride;
- /** if true, invoke resolveObject() */
- private boolean enableResolve;
- /**
- * Context during upcalls to class-defined readObject methods; holds
- * object currently being deserialized and descriptor for current class.
- * Null when not during readObject upcall.
- */
- private SerialCallbackContext curContext;
- vlist——java.io.ObjectInputStream.ValidationList
该成员属性的概念类型为一个“验证链”,它主要用于提供一个回调操作的验证集合; - closed——boolean
判断当前字节流是否已经处于“closed”状态,如果已经是“closed”了,则不需要再从字节流中读取数据; - defaultDataEnd——boolean
该标记表示一个数据段的结束,针对Data Block模式的数据段而言它的结束会出现一个TC_ENDBLOCKDATA标记,该成员属性是用于判断非Data Block模式的数据段的结束——这种情况下无法读取TC_ENDBLOCKDATA来判断; - primVals——byte[]
该成员属性表示从字节流中读取的所有对象中基础类型的字段值的集合,前文分析二进制序列时已经知道,对象中的成员属性值是直接输出的,它没有任何标记来识别,而primVals成员属性中就存储了所有基础类型的成员属性的值的集合;
- /**
- * Converts specified span of bytes into float values.
- */
- // REMIND: remove once hotspot inlines Float.intBitsToFloat
- private static native void bytesToFloats(byte[] src, int srcpos,
- float[] dst, int dstpos,
- int nfloats);
- /**
- * Converts specified span of bytes into double values.
- */
- // REMIND: remove once hotspot inlines Double.longBitsToDouble
- private static native void bytesToDoubles(byte[] src, int srcpos,
- double[] dst, int dstpos,
- int ndoubles);
- /**
- * Returns the first non-null class loader (not counting class loaders of
- * generated reflection implementation classes) up the execution stack, or
- * null if only code from the null class loader is on the stack. This
- * method is also called via reflection by the following RMI-IIOP class:
- *
- * com.sun.corba.se.internal.util.JDKClassLoader
- *
- * This method should not be removed or its signature changed without
- * corresponding modifications to the above class.
- */
- // REMIND: change name to something more accurate?
- private static native ClassLoader latestUserDefinedLoader();
- public boolean readBoolean() throws IOException {
- return bin.readBoolean();
- }
- public byte readByte() throws IOException {
- return bin.readByte();
- }
- public int readUnsignedByte() throws IOException {
- return bin.readUnsignedByte();
- }
- public char readChar() throws IOException {
- return bin.readChar();
- }
- public short readShort() throws IOException {
- return bin.readShort();
- }
- public int readUnsignedShort() throws IOException {
- return bin.readUnsignedShort();
- }
- public int readInt() throws IOException {
- return bin.readInt();
- }
- public long readLong() throws IOException {
- return bin.readLong();
- }
- public float readFloat() throws IOException {
- return bin.readFloat();
- }
- public double readDouble() throws IOException {
- return bin.readDouble();
- }
- public void readFully(byte[] buf) throws IOException {
- bin.readFully(buf, 0, buf.length, false);
- }
- public void readFully(byte[] buf, int off, int len) throws IOException {
- int endoff = off + len;
- if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) {
- throw new IndexOutOfBoundsException();
- }
- bin.readFully(buf, off, len, false);
- }
- public int skipBytes(int len) throws IOException {
- return bin.skipBytes(len);
- }
- @Deprecated
- public String readLine() throws IOException {
- return bin.readLine();
- }
- public String readUTF() throws IOException {
- return bin.readUTF();
- }
- public int available() throws IOException {
- return bin.available();
- }
- public void close() throws IOException {
- /*
- * Even if stream already closed, propagate redundant close to
- * underlying stream to stay consistent with previous implementations.
- */
- closed = true;
- if (depth == 0) {
- clear();
- }
- bin.close();
- }
- public ObjectInputStream.GetField readFields()
- throws IOException, ClassNotFoundException
- {
- if (curContext == null) {
- throw new NotActiveException("not in call to readObject");
- }
- Object curObj = curContext.getObj();
- ObjectStreamClass curDesc = curContext.getDesc();
- bin.setBlockDataMode(false);
- GetFieldImpl getField = new GetFieldImpl(curDesc);
- getField.readFields();
- bin.setBlockDataMode(true);
- if (!curDesc.hasWriteObjectData()) {
- /*
- * Fix for 4360508: since stream does not contain terminating
- * TC_ENDBLOCKDATA tag, set flag so that reading code elsewhere
- * knows to simulate end-of-custom-data behavior.
- */
- defaultDataEnd = true;
- }
- return getField;
- }
- private IOException readFatalException() throws IOException {
- if (bin.readByte() != TC_EXCEPTION) {
- throw new InternalError();
- }
- clear();
- return (IOException) readObject0(false);
- }
- private Object readNull() throws IOException {
- if (bin.readByte() != TC_NULL) {
- throw new InternalError();
- }
- passHandle = NULL_HANDLE;
- return null;
- }
- private Object readHandle(boolean unshared) throws IOException {
- if (bin.readByte() != TC_REFERENCE) {
- throw new InternalError();
- }
- passHandle = bin.readInt() - baseWireHandle;
- if (passHandle < 0 || passHandle >= handles.size()) {
- throw new StreamCorruptedException(
- String.format("invalid handle value: %08X", passHandle +
- baseWireHandle));
- }
- if (unshared) {
- // REMIND: what type of exception to throw here?
- throw new InvalidObjectException(
- "cannot read back reference as unshared");
- }
- Object obj = handles.lookupObject(passHandle);
- if (obj == unsharedMarker) {
- // REMIND: what type of exception to throw here?
- throw new InvalidObjectException(
- "cannot read back reference to unshared object");
- }
- return obj;
- }
- java.io.StreamCorruptedException
如果passHandle的值大于handles中的尺寸,或者通过和baseWireHandle计算过后的值小于0,则抛出该异常信息; - java.io.InvalidObjectException
如果调用readHandle方法时使用了“unshared”的方法则抛出该异常信息; - java.io.InvalidObjectException
如果从handles中获取的对象和unsharedMarker引用的对象是同一个对象则抛出该异常信息;
- private Class readClass(boolean unshared) throws IOException {
- if (bin.readByte() != TC_CLASS) {
- throw new InternalError();
- }
- ObjectStreamClass desc = readClassDesc(false);
- Class cl = desc.forClass();
- passHandle = handles.assign(unshared ? unsharedMarker : cl);
- ClassNotFoundException resolveEx = desc.getResolveException();
- if (resolveEx != null) {
- handles.markException(passHandle, resolveEx);
- }
- handles.finish(passHandle);
- return cl;
- }
- private ObjectStreamClass readProxyDesc(boolean unshared)
- throws IOException
- {
- if (bin.readByte() != TC_PROXYCLASSDESC) {
- throw new InternalError();
- }
- ObjectStreamClass desc = new ObjectStreamClass();
- int descHandle = handles.assign(unshared ? unsharedMarker : desc);
- passHandle = NULL_HANDLE;
- int numIfaces = bin.readInt();
- String[] ifaces = new String[numIfaces];
- for (int i = 0; i < numIfaces; i++) {
- ifaces[i] = bin.readUTF();
- }
- Class cl = null;
- ClassNotFoundException resolveEx = null;
- bin.setBlockDataMode(true);
- try {
- if ((cl = resolveProxyClass(ifaces)) == null) {
- resolveEx = new ClassNotFoundException("null class");
- }
- } catch (ClassNotFoundException ex) {
- resolveEx = ex;
- }
- skipCustomData();
- desc.initProxy(cl, resolveEx, readClassDesc(false));
- handles.finish(descHandle);
- passHandle = descHandle;
- return desc;
- }
- 先判断TC_PROXYCLASSDESC标记看是否需要抛出InternalError;
- 如果使用的读取模式是非共享模式,则将从引用->对象的映射中读取一个新的desc,否则直接读取unsharedMarker中读取对应的对象;
- 构建接口名称数组ifaces;
- 开启Data Block模式然后调用resolveProxyClass方法处理每一个接口内容;
- 调用skipCustomData方法跳过自定义信息的读取;
- 调用ObjectStreamClass中的initProxy方法来初始化cl;
- 调用handles的finish方法完成引用Handle的赋值操作,将结果赋值给passHandle成员属性;
- private ObjectStreamClass readNonProxyDesc(boolean unshared)
- throws IOException
- {
- if (bin.readByte() != TC_CLASSDESC) {
- throw new InternalError();
- }
- ObjectStreamClass desc = new ObjectStreamClass();
- int descHandle = handles.assign(unshared ? unsharedMarker : desc);
- 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);
- try {
- if ((cl = resolveClass(readDesc)) == null) {
- resolveEx = new ClassNotFoundException("null class");
- }
- } catch (ClassNotFoundException ex) {
- resolveEx = ex;
- }
- skipCustomData();
- desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));
- handles.finish(descHandle);
- passHandle = descHandle;
- return desc;
- }
- 先判断TC_CLASSDESC标记看是否需要抛出InternalError;
- 如果使用的读取模式是非共享模式,则将从引用->对象的映射中读取一个新的desc,否则直接读取unsharedMarker中读取对应的对象;
- 调用readClassDescriptor方法读取当前类的元数据信息;
- 开启Data Block模式然后调用resolveClass方法处理当前类的信息;
- 调用skipCustomData方法跳过自定义信息的读取;
- 调用ObjectStreamClass中的initNonProxy方法初始化cl;
- 调用handles的finish方法完成引用Handle的赋值操作,将结果赋值给passHandle成员属性;
- private String readString(boolean unshared) throws IOException {
- String str;
- byte tc = bin.readByte();
- switch (tc) {
- case TC_STRING:
- str = bin.readUTF();
- 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.finish(passHandle);
- return str;
- }
- 先从系统中读取字节标记:
如果标记值为TC_STRING,则调用readUTF方法读取String信息;
如果标记值为TC_LONGSTRING,则调用readLongUTF方法读取String信息;
如果两个标记都不是则抛出java.io.StreamCorruptedException异常信息; - 调用handles的assign方法完成引用Handle的赋值操作,将结果赋值给passHandle成员属性,然后调用finish方法;
- private Object readArray(boolean unshared) throws IOException {
- if (bin.readByte() != TC_ARRAY) {
- throw new InternalError();
- }
- ObjectStreamClass desc = readClassDesc(false);
- int len = bin.readInt();
- Object array = null;
- Class cl, ccl = null;
- if ((cl = desc.forClass()) != null) {
- ccl = cl.getComponentType();
- array = Array.newInstance(ccl, len);
- }
- int arrayHandle = handles.assign(unshared ? unsharedMarker : array);
- ClassNotFoundException resolveEx = desc.getResolveException();
- if (resolveEx != null) {
- handles.markException(arrayHandle, resolveEx);
- }
- if (ccl == null) {
- for (int i = 0; i < len; i++) {
- readObject0(false);
- }
- } else if (ccl.isPrimitive()) {
- if (ccl == Integer.TYPE) {
- bin.readInts((int[]) array, 0, len);
- } else if (ccl == Byte.TYPE) {
- bin.readFully((byte[]) array, 0, len, true);
- } else if (ccl == Long.TYPE) {
- bin.readLongs((long[]) array, 0, len);
- } else if (ccl == Float.TYPE) {
- bin.readFloats((float[]) array, 0, len);
- } else if (ccl == Double.TYPE) {
- bin.readDoubles((double[]) array, 0, len);
- } else if (ccl == Short.TYPE) {
- bin.readShorts((short[]) array, 0, len);
- } else if (ccl == Character.TYPE) {
- bin.readChars((char[]) array, 0, len);
- } else if (ccl == Boolean.TYPE) {
- bin.readBooleans((boolean[]) array, 0, len);
- } else {
- throw new InternalError();
- }
- } else {
- Object[] oa = (Object[]) array;
- for (int i = 0; i < len; i++) {
- oa[i] = readObject0(false);
- handles.markDependency(arrayHandle, passHandle);
- }
- }
- handles.finish(arrayHandle);
- passHandle = arrayHandle;
- return array;
- }
- 先从系统中读取TC_ARRAY标记,判断是否抛出InternalError;
- 读取Array数组的类描述信息【元数据信息】;
- 然后读取Array数组的长度;
- 从系统中读取Array数组中元素的类型信息;
- 从handles成员属性中得到当前数组的整数引用Handle信息;
- 调用ObjectStreamClass中的getResolveException获取“Resolve”过程中的异常信息,如果出现异常则调用markException方法标记;
- 如果Array数组中的元素是基础类型则执行基础类型的反序列化操作,若获取的类型为null则调用readObject0方法反序列化该对象,否则先调用readObject0方法反序列化该对象,然后调用markDependency标记依赖;
- 调用handles的finish方法完成引用Handle的赋值操作,将结果赋值给passHandle成员属性;
- private Enum readEnum(boolean unshared) throws IOException {
- if (bin.readByte() != TC_ENUM) {
- throw new InternalError();
- }
- ObjectStreamClass desc = readClassDesc(false);
- if (!desc.isEnum()) {
- throw new InvalidClassException("non-enum class: " + desc);
- }
- int enumHandle = handles.assign(unshared ? unsharedMarker : null);
- ClassNotFoundException resolveEx = desc.getResolveException();
- if (resolveEx != null) {
- handles.markException(enumHandle, resolveEx);
- }
- String name = readString(false);
- Enum en = null;
- Class cl = desc.forClass();
- if (cl != null) {
- try {
- en = Enum.valueOf(cl, name);
- } catch (IllegalArgumentException ex) {
- throw (IOException) new InvalidObjectException(
- "enum constant " + name + " does not exist in " +
- cl).initCause(ex);
- }
- if (!unshared) {
- handles.setObject(enumHandle, en);
- }
- }
- handles.finish(enumHandle);
- passHandle = enumHandle;
- return en;
- }
- 从系统中读取TC_ENUM标记,判断是否抛出InternalError;
- 从系统中读取Enum类型的类描述信息,如果发现类型不为Enum则抛出java.io.InvalidClassException异常;
- 从handles成员属性中得到当前数组的整数引用Handle信息;
- 调用ObjectStreamClass中的getResolveException获取“Resolve”过程中的异常信息,如果出现异常则调用markException方法标记;
- 从系统中读取枚举常量的字符串字面量【调用name()方法得到的字符串】;
- 实例化读取到的Enum类型;
- 调用handles的finish方法完成引用Handle的赋值操作,将结果赋值给passHandle成员属性;
- private Object readOrdinaryObject(boolean unshared)
- throws IOException
- {
- if (bin.readByte() != TC_OBJECT) {
- throw new InternalError();
- }
- ObjectStreamClass desc = readClassDesc(false);
- desc.checkDeserialize();
- 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;
- }
- 从系统中读取TC_OBJECT标记,判断是否抛出InternalError;
- 检查当前处理的对象是否是一个可反序列化对象【即检查它是否具有“可序列化的语义”】;
- 从系统中读取当前Java对象所属类的描述信息【类元数据信息】;
- 调用handles的finish方法完成引用Handle的赋值操作,将结果赋值给passHandle成员属性;
- 调用ObjectStreamClass中的getResolveException获取“Resolve”过程中的异常信息,如果出现异常则调用markException方法标记;
- 如果当前对象实现了Externalizable接口则调用readExternalData将对象数据写入该对象,若它实现的是Serializable接口则调用readSerialData方法执行反序列化;
- 调用handles的finish方法完成引用Handle的赋值操作;
- 判断读取对象是否实现了readResolve方法,如果实现了该方法并且引用Handle并没指向一个异常信息且获得的对象不为null时,则调用该对象的readResolve方法;
- private void readExternalData(Externalizable obj, ObjectStreamClass desc)
- throws IOException
- {
- SerialCallbackContext oldContext = curContext;
- curContext = null;
- try {
- boolean blocked = desc.hasBlockExternalData();
- if (blocked) {
- bin.setBlockDataMode(true);
- }
- if (obj != null) {
- try {
- obj.readExternal(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 readExternal
- * method has cons'ed and thrown a new CNFException of its
- * own.
- */
- handles.markException(passHandle, ex);
- }
- }
- if (blocked) {
- skipCustomData();
- }
- } finally {
- curContext = oldContext;
- }
- /*
- * At this point, if the externalizable data was not written in
- * block-data form and either the externalizable class doesn't exist
- * locally (i.e., obj == null) or readExternal() just threw a
- * CNFException, then the stream is probably in an inconsistent state,
- * since some (or all) of the externalizable data may not have been
- * consumed. Since there's no "correct" action to take in this case,
- * we mimic the behavior of past serialization implementations and
- * blindly hope that the stream is in sync; if it isn't and additional
- * externalizable data remains in the stream, a subsequent read will
- * most likely throw a StreamCorruptedException.
- */
- }
- 和ObjectOutputStream中的writeExternal方法一样,它的调用需要将上下文环境变量curContext设置成null,调用结束后再还原;
- 先查看读取的字节流中是否包含了外部化的Data Block模式写入的数据,这个检测通过调用ObjectStreamClass的hasBlockExternalData方法完成,如果包含则先启用Data Block模式;
- 若传入的对象不为null,则调用对象本身的readExternal方法【外部化的类必须实现该方法】;
- readExternal方法调用完成过后,如果字节流中使用的模式是Data Block模式则直接跳过自定义数据段的读取;
- 异常部分的注释不解释了,这里看看最后这部分的注释:如果外部化的数据并没有以Data Block的形式写入字节流或者外部化的类并不存在于本地JVM环境,又或者readExternal方法引起了CNFException异常,当前字节流会因为外部化的数据并没有处理而处于“inconsistent【不一致】”的状态。如果环境中不存在“correct【修复】”行为,系统将模仿序列化实现的行为并且盲目地系统该字节流会同步数据而一致,如果字节流中并没有外部化数据,则随后的调用将会抛出java.io.StreamCorruptedException异常信息;
- 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);
- }
- }
- }
- }
- 反序列化该对象之前,先从类描述信息【元数据信息】中获取ClassDataSlot的信息;
- 若ClassDataSlot的成员属性hasData标记有数据,则判断该反序列化对象是否重写了readObject方法
a.如果重写了该方法,则开启Data Block模式,再调用该对象的readObject方法,调用过后关闭Data Block模式;
b.如果没有重写该方法,则直接调用defaultReadFields方法读取该类中的每一个字段的值; - 如果ObjectOutputStream类使用writeObject方法写入了额外的自定义信息,则调用skipCustomData方法跳过读取,否则直接关闭Data Block模式;
- 如果ClassDataSlot的成员属性hasData标记没有序列化数据,则调用invokeReadObjectNoData方法来处理当前对象;
- public void defaultReadObject()
- throws IOException, ClassNotFoundException
- {
- if (curContext == null) {
- throw new NotActiveException("not in call to readObject");
- }
- Object curObj = curContext.getObj();
- ObjectStreamClass curDesc = curContext.getDesc();
- bin.setBlockDataMode(false);
- defaultReadFields(curObj, curDesc);
- bin.setBlockDataMode(true);
- if (!curDesc.hasWriteObjectData()) {
- /*
- * Fix for 4360508: since stream does not contain terminating
- * TC_ENDBLOCKDATA tag, set flag so that reading code elsewhere
- * knows to simulate end-of-custom-data behavior.
- */
- defaultDataEnd = true;
- }
- ClassNotFoundException ex = handles.lookupException(passHandle);
- if (ex != null) {
- throw ex;
- }
- }
- 这个方法只能在readObject方法内部调用,所以会检查curContext环境,如果上下文调用环境不对则抛出NotActiveException异常信息;
- 从上下文环境获取对象以及对象所属类的类描述信息;
- 关闭Data Block模式,并且调用defaultReadFields方法读取该类的字段信息,调用完成过后再开启Data Block模式;
- 检查对象是否重写了writeObject方法,如果没有重写该方法需要手动设置defaultDataEnd为true,从注释可以知道这段代码的目的是为了修复4360508的Bug而存在;
- 最后使用handles查找是否出现了ClassNotFoundException异常,如果出现该异常则表示对象重建过程失败,抛出该异常信息;
- private void defaultReadFields(Object obj, ObjectStreamClass desc)
- throws IOException
- {
- // REMIND: is isInstance check necessary?
- 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);
- }
- 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++) {
- ObjectStreamField f = fields[numPrimFields + i];
- objVals[i] = readObject0(f.isUnshared());
- if (f.getField() != null) {
- handles.markDependency(objHandle, passHandle);
- }
- }
- if (obj != null) {
- desc.setObjFieldValues(obj, objVals);
- }
- passHandle = objHandle;
- }
- 先从传入的desc中获得类描述信息,如果传入对象和类不匹配则抛出java.lang.ClassCastException异常信息;
- 然后从desc中读取基本类型字段信息,并且读取基本字段的信息;
- 最后读取对象类型的字段信息,调用readObject0方法读取这些字段数据;
- 在读取过程中如果从ObjectStreamField得到的字段信息不为空还需要调用markDependency方法标记依赖关系;
- 若对象信息不为空则设置其获取到的所有字段【基础类型和对象类型】的值;
- protected ObjectStreamClass readClassDescriptor()
- throws IOException, ClassNotFoundException
- {
- ObjectStreamClass desc = new ObjectStreamClass();
- desc.readNonProxy(this);
- return desc;
- }
- String readTypeString() throws IOException {
- int oldHandle = passHandle;
- try {
- byte tc = bin.peekByte();
- switch (tc) {
- case TC_NULL:
- return (String) readNull();
- case TC_REFERENCE:
- return (String) readHandle(false);
- case TC_STRING:
- case TC_LONGSTRING:
- return readString(false);
- default:
- throw new StreamCorruptedException(
- String.format("invalid type code: %02X", tc));
- }
- } finally {
- passHandle = oldHandle;
- }
- }
- TC_NULL
如果读取了TC_NULL标记表示当前String对象是一个空引用; - TC_REFERENCE
如果读取了TC_REFERENCE标记表示读取的String对象是一个String的引用,这个对象之前已经在字节流中出现过; - TC_STRING 或 TC_LONGSTRING
如果读取了TC_STRING或TC_LONGSTRING标记表示读取的是一个String的对象,调用readString方法来完成反序列化操作;
- private ObjectStreamClass readClassDesc(boolean unshared)
- throws IOException
- {
- byte tc = bin.peekByte();
- 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));
- }
- }
- 如果读取的标记为TC_NULL则直接调用readNull方法;
- 如果读取的标记为TC_REFERENCE则调用readHandle方法;
- 如果读取的标记为TC_PROXYCLASSDESC则调用readProxyDesc方法返回动态代理类的类描述符;
- 如果读取的标记为TC_CLASSDESC则调用readNonProxyDesc方法返回反序列化类的描述符;
- 条件都不满足则抛出java.io.StreamCorruptedException异常;
- 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();
- }
- }
- }
- protected Object readObjectOverride()
- throws IOException, ClassNotFoundException
- {
- return null;
- }
- 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++;
- 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);
- }
- }
- 先获取当前读取模式,检查是否是Data Block模式读取;
- 如果是Data Block模式,则先计算字节流中剩余的字节数量,剩余数量大于0或者defaultDataEnd的值为true【没有数据的情况】则抛出java.io.OptionalDataException异常信息——因为这个方法主要负责读取对象类型的数据,这些数据虽然本身是一个Data Block,但是在字节流中它并没有使用TC_BLOCKDATA标记,以及TC_BLOCKDATALONG标记开始,所以这个地方一旦发现还存在这两种类型的Data Block数据段则直接抛出异常;
计算完成过后关闭Data Block模式; - 从字节流中看是否可读取到TC_RESET标记,如果读取到了该标记则调用handleReset方法;
- 根据读取的标记执行反序列化运算:
如果读取TC_NULL——调用readNull函数;
如果读取TC_REFERENCE——调用readHandle函数;
如果读取TC_CLASS——调用readClass函数;
如果读取TC_CLASSDESC或TC_PROXYCLASSDESC——调用readClassDesc函数;
如果读取TC_STRING或TC_LONGSTRING——调用readString函数;
如果读取TC_ARRAY——调用readArray函数;
如果读取TC_ENUM——调用readEnum函数;
如果读取TC_OBJECT——调用readOrdinaryObject函数;
如果读取TC_EXCEPTION——调用readFatalExcception函数;
如果读取TC_BLOCKDATA或TC_BLOCKDATALONG——抛出异常信息,只是Data Block模式不同则抛出的异常信息不一样,开启Data Block模式;
如果读取TC_ENDBLOCKDATA——抛出异常信息,同上,只是不开启Data Block模式;
其他情况直接抛出异常信息;
- 需要注意的是在第四步中TC_ARRAY,TC_ENUM,TC_OBJECT,TC_STRING以及TC_LONGSTRING几种标记会对读取的返回结果调用checkResolve方法以检查反序列化的对象中是否重写了readResolve方法,如果重写了需要执行“Resolve”流程;
- 最后还原Data Block的原始模式,第一步读取的是什么值就还原成什么值;
- 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));
- }
- }
- public Object readUnshared() throws IOException, ClassNotFoundException {
- // if nested read, passHandle contains handle of enclosing object
- int outerHandle = passHandle;
- try {
- Object obj = readObject0(true);
- 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();
- }
- }
- }
- protected Class<?> resolveClass(ObjectStreamClass desc)
- throws IOException, ClassNotFoundException
- {
- String name = desc.getName();
- try {
- return Class.forName(name, false, latestUserDefinedLoader());
- } catch (ClassNotFoundException ex) {
- Class<?> cl = primClasses.get(name);
- if (cl != null) {
- return cl;
- } else {
- throw ex;
- }
- }
- }
- protected Class<?> resolveProxyClass(String[] interfaces)
- throws IOException, ClassNotFoundException
- {
- ClassLoader latestLoader = latestUserDefinedLoader();
- ClassLoader nonPublicLoader = null;
- boolean hasNonPublicInterface = false;
- // define proxy in class loader of non-public interface(s), if any
- Class[] classObjs = new Class[interfaces.length];
- for (int i = 0; i < interfaces.length; i++) {
- Class cl = Class.forName(interfaces[i], false, latestLoader);
- if ((cl.getModifiers() & Modifier.PUBLIC) == 0) {
- if (hasNonPublicInterface) {
- if (nonPublicLoader != cl.getClassLoader()) {
- throw new IllegalAccessError(
- "conflicting non-public interface class loaders");
- }
- } else {
- nonPublicLoader = cl.getClassLoader();
- hasNonPublicInterface = true;
- }
- }
- classObjs[i] = cl;
- }
- try {
- return Proxy.getProxyClass(
- hasNonPublicInterface ? nonPublicLoader : latestLoader,
- classObjs);
- } catch (IllegalArgumentException e) {
- throw new ClassNotFoundException(null, e);
- }
- }
- protected Object resolveObject(Object obj) throws IOException {
- return obj;
- }
- resolveClass
该方法根据字节流中读取的类描述信息加载本地类,子类如果实现了这个方法则有可能读取源有所变化,默认实现是从字节流中读取,但重写了该方法可变换读取源;ObjectOutputStream中的annotateClass方法与之对应,和annotateClass方法一样该方法在字节流中针对每一个类的元数据信息只会调用唯一的一次,它虽然可以被子类重写和修改,但该方法的最终返回值必须是一个Class类型的对象;一旦返回过后,如果Class描述的对象不是一个array数组,则计算这个类中的serialVersionUID看是否匹配,不匹配则抛出java.lang.InvalidClassException异常;
这个方法的默认实现中第三个参数是ClassLoader,这个ClassLoader会执行下边的步骤:
a.如果栈顶发现的ClassLoader是用户自定义的一个ClassLoader,它会采取就近原则获取该方法帧数据中最近的ClassLoader;
b.否则直接返回null表示当前ClassLoader是一个空引用;
在调用Class.forName时若出现了ClassNotFoundException异常信息直接抛出该异常信息;如果传入的基础类型是java关键字或void,则这个Class会表示一个基础类型的数据,使用封装类型代替,若是void则直接返回。(如果resolve时发现int类型,则直接返回Integer.TYPE类型,否则直接抛出异常信息) - resolveProxyClass
该类主要处理动态代理接口,子类可重写用于解析动态代理数据信息,该类的默认实现这里不解析; - resolveObject
该类允许ObjectInputStream的可信任子类在反序列化过程中替换另外一个对象。默认情况下“Resolve”功能是关闭的,一旦调用了enableResolveObject方法就会启用“Resolve”功能,enableResolveObject方法在调用到时候会检查字节流中用于Resolve的对象是否是可信任的。每一个可序列化对象的引用都会传入resolveObject。为了确保对象私有状态不会暴露,只有可信任的字节流可使用resolveObject方法;
a.这个方法会在一个Java对象从字节流中读取过后,在该方法调用readObject方法之前调用,默认的resolveObject方法仅仅是返回了同样的对象;
b.如果子类正在替换对象,则它必须保证替代对象是和引用指向的对象兼容的。如果对象类型并不是字段的子类或者数组类的元素则中断反序列化过程抛出异常,这种情况下恢复Java对象失败;
c.这个方法仅仅在第一次读取同类型的Java对象时调用,之后读取到的该对象的引用则直接指向原始对象;
- protected boolean enableResolveObject(boolean enable)
- throws SecurityException
- {
- if (enable == enableResolve) {
- return enable;
- }
- if (enable) {
- SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- sm.checkPermission(SUBSTITUTION_PERMISSION);
- }
- }
- enableResolve = enable;
- return !enableResolve;
- }
- 如果该功能启用,并且传入参数为true,则什么都不做直接返回;
- 如果想要启用“Resolve”功能,则需要调用Java的安全管理器并检查代码执行权限SUBSTITUTION_PERMISSION;
- public ObjectInputStream(InputStream in) throws IOException {
- verifySubclass();
- bin = new BlockDataInputStream(in);
- handles = new HandleTable(10);
- vlist = new ValidationList();
- enableOverride = false;
- readStreamHeader();
- bin.setBlockDataMode(true);
- }
- protected ObjectInputStream() throws IOException, SecurityException {
- SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
- }
- bin = null;
- handles = null;
- vlist = null;
- enableOverride = true;
- }
- 调用安全管理器SecurityManager,并且使用安全管理器检查权限SUBCLASS_IMPLEMENTATION_PERMISSION;
- 设置成员属性的默认值:
bin——null、handles——null、vlist——null、enableOverride——true
- 调用verifySubclass方法验证子类信息;
- 初始化bin成员,实例化一个BlockDataInputStream;
- 初始化handles和vlist,创建两个对应的实例;
- 将enableOverride的值设成false;
- 调用readStreamHeader方法读取魔数和序列化版本信息;
- 开启Data Block模式读取信息;
- package org.susan.java.serial;
- import java.io.FileOutputStream;
- import java.io.ObjectOutputStream;
- import java.io.Serializable;
- public class ArraySerial implements Serializable{
- /**
- *
- */
- private static final long serialVersionUID = 749500769727730567L;
- private String name;
- public ArraySerial(String name,int age){
- this.name = name;
- this.age = age;
- }
- 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;
- }
- private int age;
- // 运行函数
- public static void main(String args[]) throws Exception{
- ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("array.obj"));
- // 序列化普通数组
- short[] arr = new short[4];
- for( int i = 0; i < 4; i++ ){
- short ele = (short) (Math.random() * 100);
- arr[i] = ele;
- }
- out.writeObject(arr);
- // 序列化对象数组
- ArraySerial[] objArr = new ArraySerial[4];
- for( int i = 0; i < 4; i++ ){
- objArr[i] = new ArraySerial("LangYu"+i,(int) (Math.random() * 60));
- }
- out.writeObject(objArr);
- out.flush();
- out.close();
- }
- }
- 第一步实例化一个ObjectOutputStream,在实例化时调用了它的单参数构造函数,在调用该构造函数时会优先调用verifySubclass方法。该方法执行时会在第一个return处直接返回,因为该方法中调用getClass方法时返回的Class类型就为ObjectOutputStream;
(ObjectOutputStream--> verifySubclass)
- Class cl = getClass();
- if (cl == ObjectOutputStream.class) {
- return;
- }
- verifySubclass验证完过后会初始化成员属性:bout、handles、subs、enableOverride;
(ObjectOutputStream --> <Constructor(arg)>)
- bout = new BlockDataOutputStream(out);
- handles = new HandleTable(10, (float) 3.00);
- subs = new ReplaceTable(10, (float) 3.00);
- enableOverride = false;
- 【AC ED 00 05】随后会调用writeStreamHeader方法,这个方法会往缓冲区中写入两个特殊标记STREAM_MAGIC、STREAM_VERSION;
(ObjectOutputStream --> writeStreamHeader)
- bout.writeShort(STREAM_MAGIC);
- bout.writeShort(STREAM_VERSION);
- 开启输出流的Data Block模式;
(ObjectOutputStream --> <Constructor(arg)>)
- bout.setBlockDataMode(true);
- 因为extendedDebugInfo成员属性为false,所以并未开启Debug模式;
- 调用ObjectOutputStream的公共API成员函数writeObject,writeObject会先判断enableOverride成员属性的值,该值在第二步已经设置成false了;
(ObjectOutputStream --> writeObject)
- if (enableOverride) {
- writeObjectOverride(obj);
- return;
- }
- 因为enableOverride成员属性值为false,所以直接调用writeObject0成员函数,注意传入这个函数的第二个参数;在代码中第二个参数传入了false,证明使用的Java对象序列化方式不是“unshared”的方式;
(ObjectOutputStream --> writeObject)
- writeObject0(obj, false);
- 因为写入的是新对象,所以它不满足代码写入第一块的内容:1)写入引用并非null引用;2)写入的对象在原始字节流中并未出现过;3)写入的对象不是特殊对象类型【Class或ObjectStreamClass】;
(ObjectOutputStream --> writeObject0)
- 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;
- }
- 因为写入的是array对象,它的内部并没有重写writeReplace方法,所以直接跳过writeObject0方法中的enableReplace检测,同样不执行针对“替换对象”的字节写入流程;
- 除了上述的类型判断以外,接下来进入针对Array对象的写入过程,进入下边代码的第二个分支:
(ObjectOutputStream --> 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());
- }
- }
- 【75】调用writeArray方法写入array对象二进制序列段数据,writeArray有三个形参:
(ObjectOutputStream --> writeArray)
array——java.lang.Object:传入的对象本身的信息;
desc——java.io.ObjectStreamClass:传入的对象所属类的元数据信息;
unshared——boolean:传入对象序列化的方式,从外层writeObject方法中可以知道这个值是false的;
先调用bout成员函数的writeBytes方法写入TC_ARRAY标记,随后调用writeClassDesc方法写入array对象所属类元数据信息;
- bout.writeByte(TC_ARRAY);
- writeClassDesc(desc, false);
- writeClassDesc方法有四个分支,这个四个分支代码这里不枚举,实际上在写入array对象所属类的元数据时,会调用最后一个分支的writeNonProxyDesc方法,只是需要注意的是在调用writeClassDesc方法时第二个参数同样传入的是一个固定的false,并没将上层调用中的“unshared”方式的值传入这里;
- 【72】writeNonProxyDesc方法会先写入TC_CLASSDESC标记,随后将该类元数据的引用Handle插入到handles成员属性中:
(ObjectOutputStream --> writeNonProxyDesc)
- bout.writeByte(TC_CLASSDESC);
- handles.assign(unshared ? null : desc);
- 判断序列化流协议的版本,不同的版本需要调用不同的方法,如果是PROTOCOL_VERSION_1协议,调用writeNonProxy方法,如果不是PROTOCOL_VERSION_1协议,则调用writeClassDescriptor方法;
(ObjectOutputStream --> writeNonProxyDesc)
- if (protocol == PROTOCOL_VERSION_1) {
- // do not invoke class descriptor write hook with old protocol
- desc.writeNonProxy(this);
- } else {
- writeClassDescriptor(desc);
- }
- 因为示例中使用的是JDK 1.5以上的JDK环境,所以使用的是非PROTOCOL_VERSION_1协议,直接调用writeClassDescriptor方法,调用时使用该方法的默认实现;
(ObjectOutputStream --> writeClassDescriptor)
- desc.writeNonProxy(this);
- 【00 02 5B 53 EF 83 2E 06 E5 5D B0 FA】写入array数组的名称和serialVersionUID的值,需要注意的是数组的类的serialVersionUID的值是调用computeDefaultSUID方法计算的,因为类型是short[],所以类名应该是“[S【5B 53】”,SUID的计算细节就不在这里重复了,读者可以根据SUID的算法自己去实践实践,计算出来的值是8个字节的long类型数据,为EF 83 2E 06 E5 5D B0 FA;可以读者一定有个疑问,这一段序列的前缀【00 02】是怎么产生的?不知读者是否记得在写入TC_CLASSDESC过后,会先写入类型名称的长度,再写入类型名称的内容——writeUTF函数会处理这个细节,00 02就是short整数类型的值(“[S”.length() ==2)。注意:这里写入的“[S”不是签名信息,而是类名信息,请读者区分类名和签名的概念;
(ObjectStreamClass --> writeNonProxy)
- out.writeUTF(name);
- out.writeLong(getSerialVersionUID());
- 为了不让读者困惑,这个地方再看看writeUTF成员函数的定义,这样就彻底理解00 02为什么会出现在这个地方了:
(ObjectOutputStream.BlockDataOutputStream--> writeUTF)
- void writeUTF(String s, long utflen) throws IOException {
- if (utflen > 0xFFFFL) {
- throw new UTFDataFormatException();
- }
- writeShort((int) utflen);
- if (utflen == (long) s.length()) {
- writeBytes(s);
- } else {
- writeUTFBody(s);
- }
- }
- 判断当前数组对应的SC_*标记,因为Java中的数组都是默认实现了Serializable接口,所以flag标记的值为ObjectStreamConstants.SC_SERIALIZABLE;
(ObjectStreamClass--> writeNonProxy)
- flags |= ObjectStreamConstants.SC_SERIALIZABLE;
- 【02】写入SC_SERIALIZABLE标记值,前文解析了过了02的含义,但是里面只是说明了02表示对象所属类是“实现了Serializable接口可支持序列化的”,那么从这里可以知道为什么是02的值了;
(ObjectStreamClass --> writeNonProxy)
- out.writeByte(flags);
- 【00 00】写入short[]类型中成员属性的数量信息,因为short[]的成员属性的数量为0,所以这里写入的是一个short类型的值,为00 00;
(ObjectStreamClass --> writeNonProxy)
- out.writeShort(fields.length);
- 因为当前这个数组中没有任何成员函数信息,所以针对每个成员函数就到此为止了,写入了当前类的元数据信息过后,会在Data Block模式下调用annotateClass方法;
(ObjectOutputStream --> writeNonProxyDesc)
- Class cl = desc.forClass();
- bout.setBlockDataMode(true);
- annotateClass(cl);
- bout.setBlockDataMode(false);
- 【78】因为当前的类描述信息已经写入完毕,所以在最后写入TC_ENDBLOCKDATA标记;
(ObjectOutputStream --> writeNonProxyDesc)
- bout.writeByte(TC_ENDBLOCKDATA);
- 在TC_ENDBLOCKDATA标记写入过后,再调用writeClassDesc写入当前Java对象的父类信息,注意2点:
1)writeNonProxyDesc调用了writeClassDesc方法,而writeClassDesc的判断条件中也会调用writeNonProxyDesc方法,这个地方是一个循环调用,使得系统可实现“从下至上递归遍历”;
2)当一个类的直接父类为Object时,getSuperDesc成员函数调用会返回null;
(ObjectOutputStream --> writeNonProxyDesc)
- writeClassDesc(desc.getSuperDesc(), false);
- 【70】在writeClassDesc中调用writeNull成员函数,这个函数会写入TC_NULL标记;
(ObjectOutputStream --> writeNull)
- bout.writeByte(TC_NULL);
- 接下来的代码段会写入该数组中的数据,先看看代码中这个数组的信息,类型为short[],长度为4,它的元素类型为基本类型(通过getComponentType获取元素的数据类型);
(ObjectOutputStream--> writeArray)
- Class ccl = desc.forClass().getComponentType();
- if (ccl.isPrimitive()) {
- // 如果元素是基础类型的代码段
- ...
- } else {
- // 如果元素是对象类型的代码段
- ...
- }
- 【00 00 00 04 00 26 00 5D 00 58 00 31】因为数组的元素是short类型,所以ccl.isPrimitive方法会返回true,随后调用bout成员函数的writeInt方法写入数组长度,在调用writeShorts方法直接将该数组中的每一个元素写入字节流;
注:这段序列的最后8个字节表示4个short值,因为每一个值调用了(short) (Math.random() * 100)方法获得,所以这8个字节表示4个随机数——所以这8个字节每一次运行时结果都不一样;
(ObjectOutputStream --> writeArray)
- short[] sa = (short[]) array;
- bout.writeInt(sa.length);
- bout.writeShorts(sa, 0, sa.length);
- 在代码最后会还原Data Block模式,并且修改depth成员属性的值;
(ObjectOutputStream --> writeObject0)
- } finally {
- depth--;
- bout.setBlockDataMode(oldMode);
- }
- 到这里主代码中out.writeObject(arr)就执行完毕了,接下来看看【对象数组】的代码流程,结合上边的【6~27】步看看两种数组的二进制序列生成的差异,前边相同的部分这里不再解析;
【75 72】写入的TC_ARRAY、TC_CLASSDESC两个标记;
【00 24】写入数组的名称长度,名称的内容为:“[Lorg.susan.java.serial.ArraySerial;”,这个字符串长度为36,换算成十六进制为24;
【5B 4C 6F 72 67 2E 73 75 73 61 6E 2E 6A 61 76 61 2E 73 65 72 69 61 6C 2E 41 72 72 61 79 53 65 72 69 61 6C 3B】字符串“[Lorg.susan.java.serial.ArraySerial;”;
【98 52 3B E3 AD 37 EE 9D】当前这个数组的serialVersionUID的值;
【02】写入的标记SC_SERIALIZABLE的值;
【00 00】表示当前这个对象数组中没有任何成员属性,所以成员属性的数量为0;
【78 70】表示标记TC_ENDBLOCKDATA、TC_NULL;
到这里对象数组的类描述信息【元数据信息】就执行完成了,和上边的步骤【11~24】的代码是一模一样的;
- 【00 00 00 04】对象数组的复杂在于针对每一个元素的写入过程,也就是从上边第25步开始有变化,调用ccl.isPrimitive方法会返回false,走入另外一个代码流程,先写入对象数组的长度信息,示例中数组长度为4;
(ObjectOutputStream --> writeArray)
- Object[] objs = (Object[]) array;
- int len = objs.length;
- bout.writeInt(len);
- 写入对象数组的长度信息过后,接着写入每一个元素的信息,因为元素的类型是对象类型,所以调用writeObject0方法,注意这部分信息的调用可开启Debug调试模式,代码中会针对extendedDebugInfo成员属性进行检测;
(ObjectOutputStream --> writeArray)
- 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();
- }
- }
- }
- writeObject0方法的主要流程这里就不阐述了,这里调用的是writeOrdinaryObject方法;
(ObjectOutputStream --> writeObject0)
- writeOrdinaryObject(obj, desc, unshared);
- 在调用该方法时,系统会先调用ObjectStreamClass类的checkSerialize方法检查数组中的元素是否可支持序列化,这里检查的类为org.susan.java.serial.ArraySerial,如果该类不支持序列化,checkSerialize方法会抛出异常信息;
(ObjectOutputStream --> writeOrdinaryObject)
- desc.checkSerialize();
- 【73】在写入数组元素时候先写入TC_OBJECT标记信息,跟随其后调用writeClassDesc方法写入该对象所属类的元数据信息,该方法内部又调用了writeNonProxyDesc方法;
(ObjectOutputStream --> writeOrdinaryObject)
- bout.writeByte(TC_OBJECT);
- writeClassDesc(desc, false);
- 【72】调用writeNonProxyDesc方法写入TC_CLASSDESC标记,随后调用writeClassDescriptor方法写入对应的类描述信息;
(ObjectOutputStream --> writeNonProxyDesc)
- bout.writeByte(TC_CLASSDESC);
- handles.assign(unshared ? null : desc);
- 【00 21 6F 72 67 2E 73 75 73 61 6E 2E 6A 61 76 61 2E 73 65 72 69 61 6C 2E 41 72 72 61 79 53 65 72 69 61 6C】在写入类描述信息时会先写入类名,类名写入调用了writeUTF方法,其格式为“长度 名称”,这段代码生成的二进制序列如下:
00 21段:类名长度为33,十六进制值为21,之后所有生成的二进制的值为字符串:“org.susan.java.serial.ArraySerial”;
(ObjectStreamClass --> writeNonProxy)
- out.writeUTF(name);
- out.writeLong(getSerialVersionUID());
- 【0A 66 C2 FA A2 80 D3 87】随后写入SUID的信息(见第35步),该serialVersionUID和上边写入SUID的值不一样,这个值是直接提取的源代码中定义的值749500769727730567;
- 【02】随后写入SC_SERIALIZABLE标记信息;
- 从这一步开始,和上边写入数组的描述信息就有所不同了,仔细看看这段写入字段信息的代码,这段代码会先写入该类中的成员属性的数量:
【00 02】该类中包含了两个字段name和age,所以这里写入的二进制数据是00 02;
【49 00 03 61 67 65】49为类型代码,值为“I”,00 03表示属性age的名称长度,61 67 65表示属性名称字符串“age”;
【4C 00 04 6E 61 6D 65】4C为类型代码,值为“L”,00 04表示属性name的名称长度,6E 61 6D 65表示属性名称字符串“name”;
注意该类中存在一个判断,在判断内部会调用writeTypeString方法,该方法会根据传入的String类型分别调用三个不同函数:
null——调用writeNull
引用Handle——调用writeHandle
String对象——调用writeString
【74 00 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B】因为这里是第一次遇到String类型的对象,所以这里会先写入String的元数据信息,74表示TC_STRING标记,00 12表示类型名的长度,最后18个字节表示类型字符串“Ljava/lang/String;”;
(ObjectStreamClass --> writeNonProxy)
- out.writeShort(fields.length);
- for (int i = 0; i < fields.length; i++) {
- ObjectStreamField f = fields[i];
- out.writeByte(f.getTypeCode());
- out.writeUTF(f.getName());
- if (!f.isPrimitive()) {
- out.writeTypeString(f.getTypeString());
- }
- }
- 【78 70】最后写入TC_ENDBLOCKDATA标记和TC_NULL标记,表示String类的元数据结束;
- 在完成元数据的写入信息过后,需要判断序列化的类是“默认序列化”还是“外部化”,如果是“外部化”调用writeExternalData方法,如果是“默认序列化”调用writeSerialData方法;
(ObjectOutputStream --> writeOrdinaryObject)
- handles.assign(unshared ? null : obj);
- if (desc.isExternalizable() && !desc.isProxy()) {
- writeExternalData((Externalizable) obj);
- } else {
- writeSerialData(obj, desc);
- }
- 在调用writeSerialData方法时会先调用默认序列化字段数据的方法defaultWriteFields;
(ObjectOutputStream --> writeOrdinaryObject)
- defaultWriteFields(obj, slotDesc);
- 在调用defaultWriteFields方法之前,会先调用ObjectStreamClass类的checkDefaultSerialize方法检查该对象所属类是否支持默认序列化机制;
(ObjectOutputStream --> defaultWriteFields)
- desc.checkDefaultSerialize();
- 【00 00 00 2F】先写入age属性的值,这个值也是60之内的一个随机数,每次运行的时候不一样;
(ObjectOutputStream --> writeOrdinaryObject)
- int primDataSize = desc.getPrimDataSize();
- if (primVals == null || primVals.length < primDataSize) {
- primVals = new byte[primDataSize];
- }
- desc.getPrimFieldValues(obj, primVals);
- bout.write(primVals, 0, primDataSize, false);
- 【74 00 07 4C 61 6E 67 59 75 30】随后调用writeObject0方法写入name属性的值,这样对象数组的第一个元素就完成了写入操作;
- 【73 71 00 7E 00 04】这段序列是数组元素中第二个元素的元数据信息,和第一个元素不一样,第一个元素写入的是TC_OBJECT标记,第二个元素写入的就是TC_REFERENCE标记了;
(ObjectOutputStream --> writeHandle)
- bout.writeByte(TC_REFERENCE);
- bout.writeInt(baseWireHandle + handle);
- 接下来就直接看看最后的几段序列的生成:
【00 00 00 26 74 00 07 4C 61 6E 67 59 75 31】第二个元素age和name的值;
【73 71 00 7E 00 04】第三个元素的引用Handle;
【00 00 00 1C 74 00 07 4C 61 6E 67 59 75 32】第三个元素age和name的值;
【73 71 00 7E 00 04】第四个元素的引用Handle;
【00 00 00 26 74 00 07 4C 61 6E 67 59 75 33】第四个元素age和name的值; - 输出了上述的序列过后整个二进制序列的生成过程就结束了,而之后的代码这儿就不详细解析了;
B0 FA 02 00 00 78 70 00 00 00 04 00 26 00 5D 00
58 00 31 75 72 00 24 5B 4C 6F 72 67 2E 73 75 73
61 6E 2E 6A 61 76 61 2E 73 65 72 69 61 6C 2E 41
72 72 61 79 53 65 72 69 61 6C 3B 98 52 3B E3 AD
37 EE 9D 02 00 00 78 70 00 00 00 04 73 72 00 21
6F 72 67 2E 73 75 73 61 6E 2E 6A 61 76 61 2E 73
65 72 69 61 6C 2E 41 72 72 61 79 53 65 72 69 61
6C 0A 66 C2 FA A2 80 D3 87 02 00 02 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 78 70
00 00 00 2F 74 00 07 4C 61 6E 67 59 75 30 73 71
00 7E 00 04 00 00 00 26 74 00 07 4C 61 6E 67 59
75 31 73 71 00 7E 00 04 00 00 00 1C 74 00 07 4C
61 6E 67 59 75 32 73 71 00 7E 00 04 00 00 00 26
74 00 07 4C 61 6E 67 59 75 33
- 这段序列中存在随机数,所以读者在运行过后生成的序列可能会和上边列出来的序列有所差异;
- 注意代码中的调试段,调试段的内容只有在序列化的时候存在,反序列化时没有提供调试部分的代码;
- 这里的代码流程解析只是解析了序列化的过程,并没有解析反序列化的数据读取过程;
- package org.susan.java.serial;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- // import java.io.IOException;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- import java.io.Serializable;
- public class CaseOne {
- // 执行函数
- public static void main(String args[]) throws Exception{
- // 序列化
- ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("caseone.obj"));
- SubClass sub = new SubClass(true,"silentbalanceyh","Yu Lang");
- out.writeObject(sub);
- out.flush();
- out.close();
- // 反序列化
- ObjectInputStream in = new ObjectInputStream(new FileInputStream("caseone.obj"));
- SubClass subNew = (SubClass)in.readObject();
- in.close();
- System.out.println("SUB:" + subNew);
- System.out.println("SUPER:" + subNew.getSuper());
- }
- }
- class SubClass extends BaseClass implements Serializable{
- private static final long serialVersionUID = 7442418880476300463L;
- private boolean gender;
- private String name;
- private String author;
- public SubClass(){
- super();
- }
- public SubClass(boolean gender,String name, String author){
- super(name,27);
- this.gender = gender;
- this.name = name;
- this.author = author;
- }
- public String getSuper(){
- return super.toString();
- }
- /* private void writeObject(ObjectOutputStream out) throws IOException{
- out.defaultWriteObject();
- out.writeObject(super.name);
- out.writeInt(age);
- }
- private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException{
- in.defaultReadObject();
- super.name = (String)in.readObject();
- age = (int)in.readInt();
- }*/
- @Override
- public String toString() {
- return "SubClass [gender=" + gender + ", name=" + name + ", author="
- + author + "]";
- }
- }
- class BaseClass{
- @Override
- public String toString() {
- return "BaseClass [name=" + name + ", age=" + age + "]";
- }
- protected String name;
- protected int age;
- public BaseClass(){
- }
- public BaseClass(String name, int age){
- this.name = name;
- this.age = age;
- }
- }
SUPER:BaseClass [name=null, age=0]
SUPER:BaseClass [name=silentbalanceyh, age=27]
- package org.susan.java.serial;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- import java.io.Serializable;
- public class CaseTwo implements Serializable{
- private static final long serialVersionUID = -5719199880473656625L;
- public CaseTwo(){
- System.out.println("Called Constructor!");
- }
- public static void main(String args[]) throws Exception{
- // 序列化
- ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("casetwo.obj"));
- CaseTwo sub = new CaseTwo();
- out.writeObject(sub);
- out.flush();
- out.close();
- // 反序列化
- ObjectInputStream in = new ObjectInputStream(new FileInputStream("casetwo.obj"));
- @SuppressWarnings("unused")
- CaseTwo subNew = (CaseTwo)in.readObject();
- in.close();
- }
- }
- package org.susan.java.serial;
- import java.io.FileOutputStream;
- import java.io.ObjectOutputStream;
- import java.io.Serializable;
- public class CaseThree {
- public static void main(String args[]) throws Exception {
- // 序列化
- ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(
- "casethree.obj"));
- BaseClass1 base = new BaseClass1();
- out.writeObject(base);
- out.flush();
- out.close();
- }
- }
- class BaseClass1 extends AClass1 implements AInterface,Serializable {
- private static final long serialVersionUID = 3530141157381778908L;
- }
- abstract class AClass1 implements CInterface {
- }
- interface AInterface extends BInterface {
- int name = 0;
- }
- interface BInterface {
- String bName = "Lang Yu";
- }
- interface CInterface {
- }
- 【AC ED 00 05】魔数和序列化版本信息;
- 【73 72】写入TC_OBJECT和TC_CLASSDESC标记;
- 【00 20 6F 72 67 2E 73 75 73 61 6E 2E 6A 61 76 61 2E 73 65 72 69 61 6C 2E 42 61 73 65 43 6C 61 73 73 31】类名的长度以及类名字符串:“org.susan.java.serial.BaseClass1”;
- 【30 FD 94 AD DC BA 2D DC】对应类的SUID的值;
- 【00 00】表示这个类没有任何属性,属性数量为0;
- 【78 70】表示TC_ENDBLOCKDATA和TC_NULL标记信息;
- AInterface base = new BaseClass1();
- CInterface base = new BaseClass1();
- BInterface base = new BaseClass1();
- AClass1 base = new BaseClass1();