writeObject序列化过程之Serializable方式源码详解(流程图)

我的个人网站 www.ryzeyang.top

内容概览
通过详细的流程图带你一步步看看序列化的核心过程做了什么?一起来探究这序列化的过程吧!
20201202082043

让我们来看下今天的主角 objectOutputStream.writeObject(s2);
没错,就是java ObjectOutputStream中的一个主要方法 writeObject,通过该方法,我们可以将对象序列化🤯。
问题:序列化到底序列化了什么?

流程图

serializable2

Answer

通过writeObject源码的注释可知,序列化时会将指定的对象写入ObjectOutputStream,具体包括以下几个

  1. 对象的字节码
  2. 类名
  3. 非transient或者非static的fields(字段)
  4. 父类

源码解读

writeObject

20200922212535
接着我们来看看writeObject0这个底层方法的实现。🤔

ObjectStreamClass

它是序列化操作的核心对象!desc贯穿整个序列化过程。
20200922224849

那它是个怎样的类呢?😵
20200922225214
这个注释的大概意思是 它是 类的序列化描述符,包含了类的名字和序列化版本号serialVersionUID,还有这个类的加载是在lookup方法中的(重点!!)。

lookup(重点)

writeObject0中有下面这段代码,就是它创建了我们的主角ObjectStreamClass的,让我们一起看下吧!
20200923074339
进入到该方法中可以看到如下的注释:

Looks up and returns class descriptor for given class, or null if class is non-serializable and “all” is set to false.

简单说就是会去创建这个ObjectStreamClass😁
在方法中可以看到如下代码
20200923074836
接着进入到它的构造器中:
20200923075023
我们这个类是serializable的,会走下面这段逻辑
20200923075241

getDeclaredSUID !

可以看到该方法会返回这个版本号serialVersionUID,没有的话返回null。😄
20200923075801

getSerialFields !

该方法的作用是:返回可以序列化的字段数组😄
20200923080533
先看第一个红框的内容getDeclaredSerialFields

getDeclaredSerialFields !!

该方法会去获取你指定的可序列化字段 同时该字段 serialPersistentFields在序列化类中必须是由private static final修饰的👈
有指定的话就不会走上图第二个红框中的方法getDefaultSerialFields

20200923080823

getDefaultSerialFields !!

这里static是8,transient是16

十进制二进制
80000 1000
160001 0000

进行或运算int mask = Modifier.STATIC | Modifier.TRANSIENT;的值用二进制表示为 0001 1000
20200923082344
这时你可能会想到那个版本号也是static修饰的 ,但是我们前面已经用这个
getDeclaredSUID方法去获取它了,所以序列化时 应该序列化了所有非static,非transient的字段还有特殊字段,版本号serialVersionUID

writeOrdinaryObject

在了解了ObjectStreamClass类后,我们继续往下查找,找到writeOrdinaryObject`方法 😄
20200922221935
进入该方法,从代码上的注释可以知道,该方法会将“普通”可序列化对象的表示形式写入流。(即不是字符串、类、ObjectStreamClass、数组或枚举常量)
20200923084504

writeClassDesc !

该方法会将ObjectStreamClass写到流中。🤖
20200923215943
接着我们看看这个writeNonProxyDesc方法
20200923221036
进入到上图红框中的方法writeClassDescriptor
20200923221138

writeNonProxy !

终于来到最核心的地方啦!,通过下面两个方法,out会将类名和它的版本号
serialVersionUID写入到流中
20200923221307

接着来到下面这里,会去写入可序列化字段ObjectStreamField的类型和字段名称(注意!这里还没有写入字段的value!写值的操作在方法writeSerialData)。
20200923221853

writeSerialData

接着进入到writeSerialData方法,该方法的目的是:为给定对象的每个可序列化类写入实例数据,从父类到子类😄
20200922223547

defaultWriteFields

接着进入到defaultWriteFields方法,该方法的目的是:获取给定对象的可序列化字段的值并将其写入流,desc会指定哪些field要写,按什么顺序写。😋
20200922232549
让我们看看该方法中的具体实现。如图所示,如果还有非基本数据类型的字段话,会再去遍历,然后又去执行writeObject0(又回到最开始啦!)😮
20200923224622

总结

对实现了可序列化接口Serializable的类,在序列化时,会先去创建ObjectStreamClass对象,该对象封装了序列化类的版本号serialVersionUID,类的全称,可序列化字段等,如果你指定了序列化的字段serialPersistentFields,则不会去获取默认的可序列化字段,反之获取默认的(即非static,非transient的)。最后通过writeNonProxy方法将类名,版本号,还有可序列化字段的类型,名称到流中,接着通过writeSerialData对这些可序列化字段进行赋值。

有什么不对的请多多指教!欢迎留言讨论😄

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值