我的个人网站 www.ryzeyang.top
内容概览
通过详细的流程图带你一步步看看序列化的核心过程做了什么?一起来探究这序列化的过程吧!
让我们来看下今天的主角 objectOutputStream.writeObject(s2);
没错,就是java ObjectOutputStream中的一个主要方法 writeObject,通过该方法,我们可以将对象序列化?。
问题:序列化到底序列化了什么?
流程图

Answer
通过writeObject源码的注释可知,序列化时会将指定的对象写入ObjectOutputStream,具体包括以下几个
- 对象的字节码
- 类名
- 非transient或者非static的fields(字段)
- 父类
源码解读
writeObject

接着我们来看看writeObject0这个底层方法的实现。?
ObjectStreamClass
它是序列化操作的核心对象!desc贯穿整个序列化过程。
那它是个怎样的类呢??
这个注释的大概意思是 它是 类的序列化描述符,包含了类的名字和序列化版本号serialVersionUID,还有这个类的加载是在lookup方法中的(重点!!)。
lookup(重点)
在writeObject0中有下面这段代码,就是它创建了我们的主角ObjectStreamClass的,让我们一起看下吧!
进入到该方法中可以看到如下的注释:
Looks up and returns class descriptor for given class, or null if class is non-serializable and “all” is set to false.
简单说就是会去创建这个ObjectStreamClass?
在方法中可以看到如下代码
接着进入到它的构造器中:
我们这个类是serializable的,会走下面这段逻辑
getDeclaredSUID !
可以看到该方法会返回这个版本号serialVersionUID,没有的话返回null。?
getSerialFields !
该方法的作用是:返回可以序列化的字段数组?
先看第一个红框的内容getDeclaredSerialFields
getDeclaredSerialFields !!
该方法会去获取你指定的可序列化字段 同时该字段 serialPersistentFields在序列化类中必须是由private static final修饰的?
有指定的话就不会走上图第二个红框中的方法getDefaultSerialFields

getDefaultSerialFields !!
这里static是8,transient是16
| 十进制 | 二进制 |
|---|---|
| 8 | 0000 1000 |
| 16 | 0001 0000 |
进行或运算int mask = Modifier.STATIC | Modifier.TRANSIENT;的值用二进制表示为 0001 1000
这时你可能会想到那个版本号也是static修饰的 ,但是我们前面已经用这个
getDeclaredSUID方法去获取它了,所以序列化时 应该序列化了所有非static,非transient的字段还有特殊字段,版本号serialVersionUID
writeOrdinaryObject
在了解了ObjectStreamClass类后,我们继续往下查找,找到writeOrdinaryObject`方法 ?
进入该方法,从代码上的注释可以知道,该方法会将“普通”可序列化对象的表示形式写入流。(即不是字符串、类、ObjectStreamClass、数组或枚举常量)
writeClassDesc !
该方法会将ObjectStreamClass写到流中。?
接着我们看看这个writeNonProxyDesc方法
进入到上图红框中的方法writeClassDescriptor
writeNonProxy !
终于来到最核心的地方啦!,通过下面两个方法,out会将类名和它的版本号
serialVersionUID写入到流中
接着来到下面这里,会去写入可序列化字段ObjectStreamField的类型和字段名称(注意!这里还没有写入字段的value!写值的操作在方法writeSerialData中)。
writeSerialData
接着进入到writeSerialData方法,该方法的目的是:为给定对象的每个可序列化类写入实例数据,从父类到子类?
defaultWriteFields
接着进入到defaultWriteFields方法,该方法的目的是:获取给定对象的可序列化字段的值并将其写入流,desc会指定哪些field要写,按什么顺序写。?
让我们看看该方法中的具体实现。如图所示,如果还有非基本数据类型的字段话,会再去遍历,然后又去执行writeObject0(又回到最开始啦!)?
总结
对实现了可序列化接口Serializable的类,在序列化时,会先去创建ObjectStreamClass对象,该对象封装了序列化类的版本号serialVersionUID,类的全称,可序列化字段等,如果你指定了序列化的字段serialPersistentFields,则不会去获取默认的可序列化字段,反之获取默认的(即非static,非transient的)。最后通过writeNonProxy方法将类名,版本号,还有可序列化字段的类型,名称到流中,接着通过writeSerialData对这些可序列化字段进行赋值。
有什么不对的请多多指教!欢迎留言讨论?
