我的个人网站 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
对这些可序列化字段进行赋值。
有什么不对的请多多指教!欢迎留言讨论😄