一直在用,但Serializable到底是怎么一回事?
阅读原文时间:2021年04月20日阅读:1

/   今日科技快讯   /

近日,阿里云宣布未来3年再投2000亿,用于云操作系统、服务器、芯片、网络等重大核心技术研发攻坚和面向未来的数据中心建设。

/   作者简介   /

本篇文章来自MxsQ的投稿,分享了他对Serializable的理解,相信会对大家有所帮助!同时也感谢作者贡献的精彩文章。

MxsQ的博客地址:

https://www.jianshu.com/u/9cf1f31e1d09

/   前言   /

在Java中,Serializable作为一种序列化手段最为方便不过,其使用成本之低,使在完全不了解它原理的情况下,均可正常使用。

需要序列化的场景很多,当涉及如果将数据从一个地方,有效地传输到另一个地方,就可涉及到序列化的使用。侧重于目标不同,实现的序列化方式也就不同,Serializable作为出镜率超高的序列化手段,自然有不同于其他序列化方式的地方。本文也主要讲出自己对于Serializable的理解,通过本文,可以得知:

  • 什么是对象序列化

  • Serializable如何实现

  • Serializable如何存储

  • Serializable有什么使用技巧

如果以上问题你不知道答案,本文或许有些帮助。

/   什么是对象序列化   /

简单来说,对象序列化,就是把运行时的对象信息,按照一定的规则,翻译成一串有迹可循的二进制流,然后将此二进制流,传输到从一方传输到另一方。

而在接收方,在接受到此二进制流之后,可以按照约定好的解析规则,进行反序列化,将对象信息解析出来,得到有用的数据信息。

对象信息,包括Class信息、继承关系、访问权限、变量类型以及数值信息等。因此,序列化后得到的二进制流,不仅仅包含了描述一个对象的Class的关键信息,也存储了具有实际意义的数值信息。因此,序列化可以描述为——转述对象信息,存储对象数据。

/   Serializable如何实现   /

既然如此,那么Serializable是如何实现序列化的呢?

对于使用来说,我们仅需要通过声明实现Serializable接口,即可使用。如果再顺嘴问一下,Serializable怎么实现,绝大多数人都能脱口而出,使用反射使用。如果再深入地问下去,为什么要使用反射实现,不少人会哑火。

在进入真正的源码讲解之前,不妨假设,如果让你来写Serializable,你会如何实现?

开发者写出的对象千差万别,并且对于Serializable的使用说来,也不会提供更多的信息。但是可以知道,每一个对象的类信息可以用一个对应的Class类型来进行描述,其中Class类型包含了一个类所的方法,成员属性,作用域,访问权限,继承关系等。

每一种类型都是Object的子类,并且在运行时,可以通过对象,访问到存储于heep中的对象数据。

既然如此,当得拿到一个将要进行序列化的对象时,可以通过反射的手段提取信息。如果将一个对象视为根节点,那么一个对象的所有信息,可以粗略地用下面一课树来表示:

上图中,Class节点可以继续往下分解,直到为Object类型。

那么剩下的问题,就是如何通过反射,遍历这棵树,提取并存储关键信息了。

ObjectStreamClass

Serializable中,使用ObjectStreamClass来描述一种对象的存在。在ObjectStreamClass中,除了Serializable提供的此对象类型可以实现以达到其他目的的几个方法外,并不关心所包含的其他方法。因为对于将要序列化传输的对象来说,关心他的数据结构,以及各属性的基础属性值。

ObjectStreamClass接受Class类型作为参数进行实例话,并提取各层信息。

&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;ObjectStreamClass(final&nbsp;Class<?>&nbsp;cl)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;代表的类类型
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.cl&nbsp;=&nbsp;cl;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;类名
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name&nbsp;=&nbsp;cl.getName();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;是否是动态代理产生的类
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;isProxy&nbsp;=&nbsp;Proxy.isProxyClass(cl);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;是否是Enum
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;isEnum&nbsp;=&nbsp;Enum.class.isAssignableFrom(cl);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;是否实现了&nbsp;serializable
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;serializable&nbsp;=&nbsp;Serializable.class.isAssignableFrom(cl);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;是否实现了&nbsp;externalizable
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;externalizable&nbsp;=&nbsp;Externalizable.class.isAssignableFrom(cl);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;父类类型
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class<?>&nbsp;superCl&nbsp;=&nbsp;cl.getSuperclass();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;superDesc是一个ObjectStreamClass,代表父类的描述
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;superDesc&nbsp;=&nbsp;(superCl&nbsp;!=&nbsp;null)&nbsp;?&nbsp;lookup(superCl,&nbsp;false)&nbsp;:&nbsp;null;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;指向自己的ObjectStreamClass
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;localDesc&nbsp;=&nbsp;this;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(serializable)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AccessController.doPrivileged(new&nbsp;PrivilegedAction<Void>()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;Void&nbsp;run()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(isEnum)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;serialVersionUID
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;suid&nbsp;=&nbsp;Long.valueOf(0);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Enum没有属性
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fields&nbsp;=&nbsp;NO_FIELDS;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;null;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(cl.isArray())&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;为集合的话,没有属性
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fields&nbsp;=&nbsp;NO_FIELDS;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;null;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;获取serialVersionUID
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;suid&nbsp;=&nbsp;getDeclaredSUID(cl)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;获取此类所有需要被序列化的属性,即Filed
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fields&nbsp;=&nbsp;getSerialFields(cl);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;computeFieldOffsets();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(InvalidClassException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;serializeEx&nbsp;=&nbsp;deserializeEx&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new&nbsp;ExceptionInfo(e.classname,&nbsp;e.getMessage());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fields&nbsp;=&nbsp;NO_FIELDS;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(externalizable)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;实现了Enternalizable则反射获取构造方法
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cons&nbsp;=&nbsp;getExternalizableConstructor(cl);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;反射获取构造方法
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cons&nbsp;=&nbsp;getSerializableConstructor(cl);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;反射获取writeObject方法,实现了则有
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeObjectMethod&nbsp;=&nbsp;getPrivateMethod(cl,&nbsp;"writeObject",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new&nbsp;Class<?>[]&nbsp;{&nbsp;ObjectOutputStream.class&nbsp;},
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Void.TYPE);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;反射获取readObject方法,实现了则有
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;readObjectMethod&nbsp;=&nbsp;getPrivateMethod(cl,&nbsp;"readObject",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new&nbsp;Class<?>[]&nbsp;{&nbsp;ObjectInputStream.class&nbsp;},
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Void.TYPE);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;反射获取readObjectNoData方法,实现了则有
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;readObjectNoDataMethod&nbsp;=&nbsp;getPrivateMethod(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cl,&nbsp;"readObjectNoData",&nbsp;null,&nbsp;Void.TYPE);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hasWriteObjectData&nbsp;=&nbsp;(writeObjectMethod&nbsp;!=&nbsp;null);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;反射获取是否实现了&nbsp;writeReplace,实现了则有
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeReplaceMethod&nbsp;=&nbsp;getInheritableMethod(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cl,&nbsp;"writeReplace",&nbsp;null,&nbsp;Object.class);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;反射获取是否实现了&nbsp;writeReplace,实现了则有
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;readResolveMethod&nbsp;=&nbsp;getInheritableMethod(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cl,&nbsp;"readResolve",&nbsp;null,&nbsp;Object.class);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;null;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;suid&nbsp;=&nbsp;Long.valueOf(0);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fields&nbsp;=&nbsp;NO_FIELDS;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;......
&nbsp;&nbsp;&nbsp;&nbsp;}

ObjectStreamClass的实例化过程可看到的关键点为:

  • Enum和集合类型是不会收集FIELDS的

  • 会获取自定义的serialVersionUID,没有则生成

  • 成员属性以ObjectStreamField来表示,通过getSerialFields()来获取,在此方法中,先通过getDeclaredSerialFields()先尝试获取通过变量serialPersistentFields声明的要进行序列化的成员属性,如果获取不到则通过getDefaultSerialFields()反射获取类型中声明的成员属性

看一眼getDefaultSerialFields()。

&nbsp;&nbsp;private&nbsp;static&nbsp;ObjectStreamField[]&nbsp;getDefaultSerialFields(Class<?>&nbsp;cl)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;获取所有&nbsp;Field
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Field[]&nbsp;clFields&nbsp;=&nbsp;cl.getDeclaredFields();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ArrayList<ObjectStreamField>&nbsp;list&nbsp;=&nbsp;new&nbsp;ArrayList<>();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;static&nbsp;和&nbsp;transient&nbsp;的掩码
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;mask&nbsp;=&nbsp;Modifier.STATIC&nbsp;|&nbsp;Modifier.TRANSIENT;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;<&nbsp;clFields.length;&nbsp;i++)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;((clFields[i].getModifiers()&nbsp;&&nbsp;mask)&nbsp;==&nbsp;0)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;添加所有不被声明为&nbsp;STATIC或&nbsp;TRANSIENT&nbsp;的&nbsp;FIELD,以ObjectStreamField来描述
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.add(new&nbsp;ObjectStreamField(clFields[i],&nbsp;false,&nbsp;true));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;size&nbsp;=&nbsp;list.size();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(size&nbsp;==&nbsp;0)&nbsp;?&nbsp;NO_FIELDS&nbsp;:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.toArray(new&nbsp;ObjectStreamField[size]);
&nbsp;&nbsp;&nbsp;&nbsp;}

获取其他各种信息也主要通过反射来获取。上文源码中看到的一些方法:

  • writeObject()

  • readObject()

  • readObjectNoData()

  • writeReplace()

  • readResolve()

即是Serializable提供的一些可自定义操作的方法入口,在下文中会提到。

ObjectStreamField

ObjectStreamClass用来描述一个对象,对象有属性。ObjectStreamField则描述了属性信息。ObjectStreamField的结构相对简单不少,它只需要描述一个对象的属性信息。

ObjectStreamField(Field&nbsp;field,&nbsp;boolean&nbsp;unshared,&nbsp;boolean&nbsp;showType)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;反射中代表属性的类,Field
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.field&nbsp;=&nbsp;field;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.unshared&nbsp;=&nbsp;unshared;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;属性名
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name&nbsp;=&nbsp;field.getName();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;属性的类类型,因为类的类型信息由ObjectStreamClass保存,因此只要知道类型即可
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class<?>&nbsp;ftype&nbsp;=&nbsp;field.getType();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;此属性的类型,是基本类型,或者是对象类型
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;type&nbsp;=&nbsp;(showType&nbsp;||&nbsp;ftype.isPrimitive())&nbsp;?&nbsp;ftype&nbsp;:&nbsp;Object.class;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;获取类标识符
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;signature&nbsp;=&nbsp;getClassSignature(ftype).intern();
&nbsp;&nbsp;&nbsp;&nbsp;}

signature记录类标识符,类标识符是指用一串字符串来表示一种类型,在当前上下文中为:

  • I: Integer

  • B: Byte

  • J: Long

  • F: Float

  • D: Double

  • S: Short

  • C: Character

  • Z: Boolean

  • V: Void

  • L…: 引用类型

L…用来表示引用类型,假如有类为com.qinx.Example,则类标识符为: L/com/qinx/Example。

HandleTable

HandleTable是Serializable中的缓存池,在序列化或者反序列化是,当遇到同一种信息时,如果缓存池中有缓存信息,则可以减少很多不必要的解析,引用到缓存池的那个信息即可。以序列化时为列,会缓存下来的信息有:

  • Class

  • ObjectStreamClass

  • String

  • Array

  • Enum

HandleTable源码可不深究,仅了解作用也可。实际上,有了上面提到ObjectStreamClass、ObjectStreamField、HandleTable以及前面提到的从一个对象中提取信息的思路,Serializable的实现能猜出个雏型。因为,知道了获取信息的思路,对象和属性的表示方式,以及过程中减少解析的手段。下一小节则是源码过程。

ObjectOutputStream

序列化的入口类为ObjectOutputStream:

&nbsp;&nbsp;&nbsp;public&nbsp;ObjectOutputStream(OutputStream&nbsp;out)&nbsp;throws&nbsp;IOException&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;校验继承权限
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;verifySubclass();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;构造类型为BlockDataOutputStream的OutputStream,解析过程中的数据会先写入bout,经过一些处理后,写入out
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bout&nbsp;=&nbsp;new&nbsp;BlockDataOutputStream(out);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;缓存
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;handles&nbsp;=&nbsp;new&nbsp;HandleTable(10,&nbsp;(float)&nbsp;3.00);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;替换表,在解析过程中,可以通过ObjectOutputStream提供的一些入口,来替换一些对象
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;subs&nbsp;=&nbsp;new&nbsp;ReplaceTable(10,&nbsp;(float)&nbsp;3.00);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;如果这个变量为true时,意味着需要重写writeObjectOverride()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;enableOverride&nbsp;=&nbsp;false;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;向bout先写入魔数以及版本号
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeStreamHeader();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;设置BlockDataOutputStream的模式,为true则数据显写入bout
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bout.setBlockDataMode(true);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;......
&nbsp;&nbsp;&nbsp;&nbsp;}

序列化的入口方法为writeObject()。

&nbsp;&nbsp;public&nbsp;final&nbsp;void&nbsp;writeObject(Object&nbsp;obj)&nbsp;throws&nbsp;IOException&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(enableOverride)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp; enableOverride为前面所说,也就是当自定义ObjectOutputStream并使用无参构造时,需要重写writeObjectOverride()来做序列化工作。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeObjectOverride(obj);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeObject0(obj,&nbsp;false);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(IOException&nbsp;ex)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(depth&nbsp;==&nbsp;0)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;说明遍历信息的方式有错误
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeFatalException(ex);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;ex;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}

一般来说,使用ObjectOutputStream()有参构造,走writeObject0()来实现序列化。还记得前面说过的提取对象信息的思路吗, 这里看到的变量depth,也就是当前遍历这颗树的节点所处的深度。下面的源码有点长,耐心看:

&nbsp;&nbsp;&nbsp;&nbsp;boolean&nbsp;oldMode&nbsp;=&nbsp;bout.setBlockDataMode(false);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;depth++;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;handle&nbsp;previously&nbsp;written&nbsp;and&nbsp;non-replaceable&nbsp;objects
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;h;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;((obj&nbsp;=&nbsp;subs.lookup(obj))&nbsp;==&nbsp;null)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;处理需要被替换的对象,如果obj不需要被替换,subs.lookup()会返回obj本身,而如果当obj需要被替换,会出现被替换为null的情况
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;这里是写入null
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeNull();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(!unshared&nbsp;&&&nbsp;(h&nbsp;=&nbsp;handles.lookup(obj))&nbsp;!=&nbsp;-1)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;如果obj已经被解析过,&nbsp;写入索引信息就好,不需要重复解析
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeHandle(h);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(obj&nbsp;instanceof&nbsp;Class)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Class类型的处理方式,不深入
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeClass((Class)&nbsp;obj,&nbsp;unshared);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(obj&nbsp;instanceof&nbsp;ObjectStreamClass)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;ObjectStreamClass类型的处理方式,不深入
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeClassDesc((ObjectStreamClass)&nbsp;obj,&nbsp;unshared);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;check&nbsp;for&nbsp;replacement&nbsp;object
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;orig&nbsp;=&nbsp;obj;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class<?>&nbsp;cl&nbsp;=&nbsp;obj.getClass();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectStreamClass&nbsp;desc;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(;;)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;REMIND:&nbsp;skip&nbsp;this&nbsp;check&nbsp;for&nbsp;strings/arrays?
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class<?>&nbsp;repCl;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;拿到当前obj的类型的描述ObjectStreamClass
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;desc&nbsp;=&nbsp;ObjectStreamClass.lookup(cl,&nbsp;true);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(!desc.hasWriteReplaceMethod()&nbsp;||
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(obj&nbsp;=&nbsp;desc.invokeWriteReplace(obj))&nbsp;==&nbsp;null&nbsp;||
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(repCl&nbsp;=&nbsp;obj.getClass())&nbsp;==&nbsp;cl)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;三个条件满足一个即可
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;1.&nbsp;此类型没有实现writeReplace()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;2.&nbsp;此类型实现了writeReplace()替换对象,但返回了空对象
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;3.&nbsp;此类型实现了writeReplace()并返回了空对象,获取被替换对象的类型
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cl&nbsp;=&nbsp;repCl;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(enableReplace)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;如果允许替换对象的话,进到这里
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;默认情况下replaceObject()返回本是,也就是说,ObjectOutputStream的子类可以重写replaceObject()来对一些对象进行操作
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;rep&nbsp;=&nbsp;replaceObject(obj);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(rep&nbsp;!=&nbsp;obj&nbsp;&&&nbsp;rep&nbsp;!=&nbsp;null)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;如果obj被替换成不同类型的对象,通过ObjectOutputStream解析出此类型的信息
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cl&nbsp;=&nbsp;rep.getClass();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;desc&nbsp;=&nbsp;ObjectStreamClass.lookup(cl,&nbsp;true);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;obj赋值为更换后的对象
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj&nbsp;=&nbsp;rep;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;如果发生替换时,要再此做前面的检查
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(obj&nbsp;!=&nbsp;orig)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;记录替换关系
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;subs.assign(orig,&nbsp;obj);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(obj&nbsp;==&nbsp;null)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;null的情况
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeNull();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(!unshared&nbsp;&&&nbsp;(h&nbsp;=&nbsp;handles.lookup(obj))&nbsp;!=&nbsp;-1)&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;类型已被解析过的情况
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeHandle(h);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(obj&nbsp;instanceof&nbsp;Class)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Class类型的情况
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeClass((Class)&nbsp;obj,&nbsp;unshared);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(obj&nbsp;instanceof&nbsp;ObjectStreamClass)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;ObjectStreamClass类型的情况
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeClassDesc((ObjectStreamClass)&nbsp;obj,&nbsp;unshared);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;下面分别是obj为String、集合、枚举、对象类型时的处理情况
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(obj&nbsp;instanceof&nbsp;String)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeString((String)&nbsp;obj,&nbsp;unshared);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(cl.isArray())&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeArray(obj,&nbsp;desc,&nbsp;unshared);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(obj&nbsp;instanceof&nbsp;Enum)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeEnum((Enum<?>)&nbsp;obj,&nbsp;desc,&nbsp;unshared);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;if&nbsp;(obj&nbsp;instanceof&nbsp;Serializable)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeOrdinaryObject(obj,&nbsp;desc,&nbsp;unshared);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(extendedDebugInfo)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;new&nbsp;NotSerializableException(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cl.getName()&nbsp;+&nbsp;"\n"&nbsp;+&nbsp;debugInfoStack.toString());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;new&nbsp;NotSerializableException(cl.getName());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;finally&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;遍历完了,恢复遍历深度信息
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;depth--;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bout.setBlockDataMode(oldMode);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

概括地说,做了以下事情:

  1. 首先会检查并处理obj被替换、被解析过、是Class类型、和ObjectStreamClass类型时的情况,此时会通过各自的处理方法,向bout想入信息。Class和ObjectStreamClass的处理情况不是本文的重点,因此不拓展。obj被替换以及被解析过的处理情况写入代码较简单,也不多贴例行代码

  2. obj没有被处理过,通过ObjectStreamClass.lookup()拿到它的类型描述。如果ObjectOutputStream重写了替换方法,则要将obj进行替换,并拿到替换的对象以及它的描述,因为后面的操作是基于真正要被写入的obj来操作的

  3. 当发生了替换,重写执行1

  4. 分别处理obj为String、集合、枚举、以及对象类型时的情况,其中前三者不难,不拓展。重点是对象类型时的处理方式。按照之前说的思路,最终当一个种类型的成员变量不再包含对象类型时,也就不再继续往下解析(处理父类的情况当作处理对象类型的情况来处理)

  5. 恢复遍历深度

因此,关注的点为ObjectStreamClass.lookup()如果拿到对象描述,以及writeOrdinaryObject()如何处理对象类型的解析。

获取对象描述ObjectStreamClass

&nbsp;static&nbsp;ObjectStreamClass&nbsp;lookup(Class<?>&nbsp;cl,&nbsp;boolean&nbsp;all)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(!(all&nbsp;||&nbsp;Serializable.class.isAssignableFrom(cl)))&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;如果all为true,所有类型都能拿到对象描述
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;否则,只有实现了Serializable的类型能拿到对象描述
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;null;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;因为对象描述ObjectStreamClass是与Class关联的,并且在JVM类可以被卸载,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;因此当类被卸载时,与Class相关的对象描述也也就无效了
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;所要移除这层关系并重新建立关系
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;processQueue(Caches.localDescsQueue,&nbsp;Caches.localDescs);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;WeakClassKey是WeakReference,弱引用Class
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WeakClassKey&nbsp;key&nbsp;=&nbsp;new&nbsp;WeakClassKey(cl,&nbsp;Caches.localDescsQueue);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;获取EntryFutrue或ObjectStreamClass
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;key实际为Class的hashCode
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Reference<?>&nbsp;ref&nbsp;=&nbsp;Caches.localDescs.get(key);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;entry&nbsp;=&nbsp;null;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(ref&nbsp;!=&nbsp;null)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;获取实际的引用类型
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;entry&nbsp;=&nbsp;ref.get();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EntryFuture&nbsp;future&nbsp;=&nbsp;null;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(entry&nbsp;==&nbsp;null)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;entry被回收了
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;创建EntryFuture,使用软引用关联
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EntryFuture&nbsp;newEntry&nbsp;=&nbsp;new&nbsp;EntryFuture();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Reference<?>&nbsp;newRef&nbsp;=&nbsp;new&nbsp;SoftReference<>(newEntry);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(ref&nbsp;!=&nbsp;null)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Caches.localDescs.remove(key,&nbsp;ref);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;当第一次解析这个类型时,或者entry被回收时,必定拿不到entry,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;因此先将key与EntryFuture相关联占位
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ref&nbsp;=&nbsp;Caches.localDescs.putIfAbsent(key,&nbsp;newRef);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(ref&nbsp;!=&nbsp;null)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;entry&nbsp;=&nbsp;ref.get();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;下面的判断条件,是要保证ref弱引用到的对象没有被卸载
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;while&nbsp;(ref&nbsp;!=&nbsp;null&nbsp;&&&nbsp;entry&nbsp;==&nbsp;null);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(entry&nbsp;==&nbsp;null)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;future&nbsp;=&nbsp;newEntry;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(entry&nbsp;instanceof&nbsp;ObjectStreamClass)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;如果拿到ObjectStreamClass,直接返回
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(ObjectStreamClass)&nbsp;entry;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(entry&nbsp;instanceof&nbsp;EntryFuture)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;如果走这里,说明是第一次获取次类型的描述,或者描述已经被卸载
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;future&nbsp;=&nbsp;(EntryFuture)&nbsp;entry;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(future.getOwner()&nbsp;==&nbsp;Thread.currentThread())&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;如果是当前线程创建的,置空
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;entry&nbsp;=&nbsp;null;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;如果是其他线程创建的,获取到其他线程创建的结果
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;entry&nbsp;=&nbsp;future.get();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(entry&nbsp;==&nbsp;null)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;说明类其他线程并未创建,创建对象描述
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;entry&nbsp;=&nbsp;new&nbsp;ObjectStreamClass(cl);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(Throwable&nbsp;th)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;entry&nbsp;=&nbsp;th;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(future.set(entry))&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;说明由当前线程创建的对象描述可用
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;与Class建立关联
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Caches.localDescs.put(key,&nbsp;new&nbsp;SoftReference<Object>(entry));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;说明当前线程穿件的对象描述不可用,其它线程已经建立好了
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;使用其他线程创建的对象描述
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;entry&nbsp;=&nbsp;future.get();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;......
&nbsp;&nbsp;&nbsp;&nbsp;}

lookup()方法最终目的,是拿到一个可用的对象描述:

  • 在开始阶段,在all为false的情况下,会检查此类型是否实现了Serializable,实现了才可进行下一步

  • 如果能拿到对象描述,需要检查是否可能,因为类可能被卸载过,描述信息失效,需要重新建立

  • 拿不到可用的对象描述,只需要通过 ObjectStreamClass(final Class cl)就可以拿到可用的对象描述。但是实际情况要复杂一些,因为在此时此刻,很可能有多个线程在序列化解析同一类型,因此需要考虑并发问题

  • 在处理并发问题时,会使用EntryFuture来对Class的关联进行占位,EntryFuture中保存了当前线程。然后,再次从缓存里获取EntryFuture,EntryFuture可用。此时,虽然之前使用当前线程使用了自己创建的EntryFuture来进行占位,但是不代表能占位成功,因此再次从缓存里获取得到的EntryFuture就是真正占位成功的对象。再从EntryFuture获取对象描述entry。需要注意的是,当前阶段保证了所有线程拿到同一个EntryFuture,但是里面的entry还不一定存在。因此这种情况下,会去尝试对象描述新建使用EntryFuture.(set)关联。关联上,说明可用;关联不上,说明其他线程已经新建关联了,EntryFuture.get()拿到真正的对象描述即可

写入对象信息

&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;void&nbsp;writeOrdinaryObject(Object&nbsp;obj,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectStreamClass&nbsp;desc,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;boolean&nbsp;unshared)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throws&nbsp;IOException
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;......
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;检查obj是不是可以进行序列化
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;desc.checkSerialize();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;写入信息,TC_OBJECT值为0x73,代表要写入一个新对象
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bout.writeByte(TC_OBJECT);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;写入对象描述信息
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeClassDesc(desc,&nbsp;false);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;如果unshared为true的话,每次都会重新解析出对象描述,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;正常情况为false,因此对象描述可以被缓存复用
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;handles.assign(unshared&nbsp;?&nbsp;null&nbsp;:&nbsp;obj);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(desc.isExternalizable()&nbsp;&&&nbsp;!desc.isProxy())&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;实现了Externalizable()的情况
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeExternalData((Externalizable)&nbsp;obj);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;实现了Serialazable的情况,前面已经写入了类型信息
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;接下来要写入数据信息
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeSerialData(obj,&nbsp;desc);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;finally&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(extendedDebugInfo)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;debugInfoStack.pop();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}

对象信息包含两方面,类型信息以及数据信息,这两种信息都会存在于序列化后的二进制流里。类型信息通过writeClassDesc()来写入,其中包含四种情况的写入规则:

  • 描述为null时

  • 描述被缓存时

  • 描述的类型时动态代理产生时

  • 描述还未曾被写入时

这里不深入writeClassDesc(),深入下去是具体的二进制流写入规则。数据信息则是通过writeSerialData()写入。

&nbsp;&nbsp;private&nbsp;void&nbsp;writeSerialData(Object&nbsp;obj,&nbsp;ObjectStreamClass&nbsp;desc)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throws&nbsp;IOException
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectStreamClass.ClassDataSlot[]&nbsp;slots&nbsp;=&nbsp;desc.getClassDataLayout();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;<&nbsp;slots.length;&nbsp;i++)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectStreamClass&nbsp;slotDesc&nbsp;=&nbsp;slots[i].desc;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(slotDesc.hasWriteObjectMethod())&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;前面有提到过,对象有一个可以操作的方法入口,writeObject()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;如果对象实现了这个方法,这里就会反射用&nbsp;writeObject()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;....
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;一般情况下,执行这个方法
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;defaultWriteFields(obj,&nbsp;slotDesc);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}

注释已解释清楚,直接看defaultWriteFields()。

&nbsp;&nbsp;private&nbsp;void&nbsp;defaultWriteFields(Object&nbsp;obj,&nbsp;ObjectStreamClass&nbsp;desc)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throws&nbsp;IOException
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class<?>&nbsp;cl&nbsp;=&nbsp;desc.forClass();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;做类型校验,保证从Obj获得的对象描述正确
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(cl&nbsp;!=&nbsp;null&nbsp;&&&nbsp;obj&nbsp;!=&nbsp;null&nbsp;&&&nbsp;!cl.isInstance(obj))&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;new&nbsp;ClassCastException();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;......

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;获取所有成员变量,在对象描述实例化时,已经完成了解析
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectStreamField[]&nbsp;fields&nbsp;=&nbsp;desc.getFields(false);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;之后对象的属性值存在这里
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object[]&nbsp;objVals&nbsp;=&nbsp;new&nbsp;Object[desc.getNumObjFields()];
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;属性个数
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;numPrimFields&nbsp;=&nbsp;fields.length&nbsp;-&nbsp;objVals.length;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;反射获取属性值
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;desc.getObjFieldValues(obj,&nbsp;objVals);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;<&nbsp;objVals.length;&nbsp;i++)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(extendedDebugInfo)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;debugInfoStack.push(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"field&nbsp;(class&nbsp;\""&nbsp;+&nbsp;desc.getName()&nbsp;+&nbsp;"\",&nbsp;name:&nbsp;\""&nbsp;+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fields[numPrimFields&nbsp;+&nbsp;i].getName()&nbsp;+&nbsp;"\",&nbsp;type:&nbsp;\""&nbsp;+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fields[numPrimFields&nbsp;+&nbsp;i].getType()&nbsp;+&nbsp;"\")");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;解析为对象类型的属性
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeObject0(objVals[i],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fields[numPrimFields&nbsp;+&nbsp;i].isUnshared());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;finally&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(extendedDebugInfo)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;debugInfoStack.pop();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}

对象描述里,存有一种类型下,所有的成员变量信息。拿到这些信息后,又依次遍历调用writeObject0()解析成员变量。这里需要注意,只有对象类型才会被遍历解析,而基本类型能直接确定。在上面的代码 desc.getNumObjFields() 处,就是获取类的为对象类型的成员变量数,此数值在对象描述实例化阶段,解析出属性后,通过computeFieldOffsets()计算。回到writeObject0()后,就是熟悉的内容了。

Serializable实现小结

  1. 将对象当成一棵树,将类型、父类、成员变量视为节点,遍历这颗树获取信息

  2. 默认情况下,通过ObjectOutputStream.writeObject0(),使用系统提供的方式解析信息;也可以通过重写writeObjectOverride()来按照自己的规则解析

  3. 通过反射解析出对象描述、属性的描述,其中成员变量的描述存于对象描述中

  4. 会以HandleTable来缓存解析信息,包括Class、ObjectStreamClass、String、Array、Enum

  5. 如果ObjectOutputStream重写了replaceObject(),可以在序列化过程中,替换某些对象

  6. 写入对象描述信息、为基本类型的成员信息,通过2-6过程,解析为对象类型的成员属性,直到某一类为Object了型或者所有成员变量为基本类型,树收敛。

/   存储   /

序列化最终的解决,是得到一组有规则的二进制流。而这部分的写入信息,分布在序列化过程代码中的边边角角,如果要把这部分信息的相关代码都贴出来,不免阅读起来失去重心,也没有营养。因此这部分的内容不会看到实际的写入代码。

如果曾了解过Class文件结构,这部分内容则非常容易消化,不了解也不妨碍理解。

Class 文件解析文章地址如下:

https://www.jianshu.com/p/74d81bac6539

简单来说,序列化出的二进制流,需要包含所序列化对象的类型描述、父类、属性描述、属性值信息,并且最终目的是要进行传输以反序列化后形成有效的数据。因此这个二进制流是紧凑的,不携带多余信息,又应该的安全的,不遗漏任何信息。除了描述、父类、属性描述、属性值这些必备的不可避免的信息外,序列化与反序列化双方必须遵守阅读此二进制流的规则,加上一些助记、索引信息,最终形成一张携带信息的索引表。

写个例子:

public&nbsp;class&nbsp;Phone&nbsp;implements&nbsp;Serializable&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;Card&nbsp;card1;
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;Card&nbsp;card2;
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;String&nbsp;brand;
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;String&nbsp;color;
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;int&nbsp;price;
&nbsp;&nbsp;&nbsp;&nbsp;.......
}

public&nbsp;class&nbsp;Card&nbsp;implements&nbsp;Serializable&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;String&nbsp;number;
&nbsp;&nbsp;&nbsp;&nbsp;......
}

public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;FILE_PATH&nbsp;=&nbsp;"info.txt";

&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;File&nbsp;f&nbsp;=&nbsp;new&nbsp;File(FILE_PATH);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectOutputStream&nbsp;out&nbsp;=&nbsp;new&nbsp;ObjectOutputStream(new&nbsp;FileOutputStream(FILE_PATH));

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Phone&nbsp;phone&nbsp;=&nbsp;new&nbsp;Phone();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;phone.setColor("0xFFFFFF");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;phone.setBrand("OnePlus");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;phone.setPrice(3000);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Card&nbsp;card1&nbsp;=&nbsp;new&nbsp;Card("12345678901");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Card&nbsp;card2&nbsp;=&nbsp;new&nbsp;Card("98765432109");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;phone.setCard1(card1);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;phone.setCard2(card2);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out.writeObject(phone);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out.flush();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out.close();

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(IOException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}

上面代码phone序列化后,写入文件中,再借用二进制工具打开这个文件后,有如下信息。

上面将二进制数据转为了十六进制,每一个位置即是一个字节码。如果没有工具,可以用ByteArrayOutputSteam来接收流,然后配合DadatypeConverter也可以在控制台输出十六进制内容。

加上阅读规则,就可以阅读以上信息。下图是我看源码过程中读出的阅读规则。

图中的含义为:

  • 带颜色方形代表一种区块

  • 颜色后面的一整块结构,代表这种颜色区块可能具有的结构

  • 数字代表占多少个字节

上面所给出的阅读规则不代表所有的规则,如果对更多的规则感兴趣,可以参考:

Serializable Specification地址:

https://docs.oracle.com/javase/7/docs/platform/serialization/spec/serialTOC.html

如果你的例子不同,可以跟上图去阅读。下面是对于当前例子序列化出的二进制的具体阅读解释。其中很多常量可以在ObjectStreamConstants中找到,如果需要自己跟写入规则,以ObjectStreamConstants来索引源码会有帮助。再附上当前情况的HandleTable表便于查看

下面每一行十六个字节码

&nbsp;AC&nbsp;ED(魔数)&nbsp;00&nbsp;05(版本)&nbsp;73(新对象)&nbsp;72(新的对象描述)&nbsp;00&nbsp;05(类名长度)&nbsp;50&nbsp;68&nbsp;6F&nbsp;6E&nbsp;65(代表Phone的ascil码)&nbsp;79&nbsp;52&nbsp;E9

&nbsp;A2&nbsp;1A&nbsp;63&nbsp;D9&nbsp;5C(Phone的serialVersionUID)&nbsp;02(代表实现了Serializable)&nbsp;00&nbsp;05(属性数量为5)&nbsp;49(属性类型为I)&nbsp;00&nbsp;05(属性名长度为5)&nbsp;70&nbsp;72&nbsp;69&nbsp;63&nbsp;65(代表price的ascil码)

&nbsp;4C(属性类型为L)&nbsp;00&nbsp;05(属性名长度为5)&nbsp;62&nbsp;72&nbsp;61&nbsp;6E&nbsp;64(代表brand的ascil码)&nbsp;74(新的String)&nbsp;00&nbsp;12(类名字面量长度为18)&nbsp;4C&nbsp;6A&nbsp;61&nbsp;76&nbsp;61

&nbsp;2F&nbsp;6C&nbsp;61&nbsp;6E&nbsp;67&nbsp;2F&nbsp;53&nbsp;74&nbsp;72&nbsp;69&nbsp;6E&nbsp;67&nbsp;3B(代表Ljava/lang/String;的ascil码)&nbsp;4C(属性类型为L)&nbsp;00&nbsp;05(属性名长度为5)

&nbsp;63&nbsp;61&nbsp;72&nbsp;64&nbsp;31(代表card1的ascil码)&nbsp;74(新的String,将指向的是后面属性类型的字面量)&nbsp;00&nbsp;06(类名字面量长度为6)&nbsp;4C&nbsp;43&nbsp;61&nbsp;72&nbsp;64&nbsp;3B(LCard;的ascil码)&nbsp;4C(属性类型为L)&nbsp;00

&nbsp;05(属性名长度5)&nbsp;63&nbsp;61&nbsp;72&nbsp;64&nbsp;32(代表card2的ascil码)&nbsp;71(已写入标记)&nbsp;00&nbsp;7E&nbsp;00&nbsp;02(这里是0x7E0000&nbsp;+&nbsp;0x02,前者代表缓存索引标记,后者代表位置,因为按照Int写入,占四字节,所以前面有00)&nbsp;4C(属性类型为L)&nbsp;00&nbsp;05(属性名长度为5)&nbsp;63&nbsp;6F

&nbsp;6C&nbsp;6F&nbsp;72(color的ascil码)&nbsp;71(已写入标记)&nbsp;00&nbsp;7E&nbsp;00&nbsp;01(缓存表第一个位置)&nbsp;78(对象结束标记)&nbsp;70(空对象)&nbsp;00&nbsp;00&nbsp;0B&nbsp;B8(int,为3000,price的值)&nbsp;74(新String)&nbsp;00

&nbsp;07(字面量长度7)&nbsp;4F&nbsp;6E&nbsp;65&nbsp;50&nbsp;6C&nbsp;75&nbsp;73(OnePlus的ascil码)&nbsp;73(新对象)&nbsp;72(新的对象描述)&nbsp;00&nbsp;04(类名)&nbsp;43&nbsp;61&nbsp;72&nbsp;64(Card的ascil码)

&nbsp;A0&nbsp;66&nbsp;51&nbsp;7E&nbsp;C3&nbsp;D5&nbsp;7D&nbsp;4A(Card的serialVersionUID)&nbsp;02(代表实现了Serializable)&nbsp;00&nbsp;01(属性数量为1)&nbsp;4C(属性类型为L)&nbsp;00&nbsp;06(属性名长度为6)&nbsp;6E&nbsp;75

&nbsp;6D&nbsp;62&nbsp;65&nbsp;72(number的ascil码)&nbsp;71(已写入标记)&nbsp;00&nbsp;7E&nbsp;00&nbsp;01(HandleTable第一个位置)&nbsp;78(对象结束)&nbsp;70(对象为空)&nbsp;74(新的String)&nbsp;00&nbsp;0B(值字面量长度为11)&nbsp;31&nbsp;32

&nbsp;33&nbsp;34&nbsp;35&nbsp;36&nbsp;37&nbsp;38&nbsp;39&nbsp;30&nbsp;31&nbsp;73(1245678901的ascil码)&nbsp;71(已写入标记)&nbsp;00&nbsp;7E&nbsp;00&nbsp;05(HandleTable第5个位置)&nbsp;74(新String)

&nbsp;00&nbsp;0B(值字面量长度11)&nbsp;39&nbsp;38&nbsp;37&nbsp;36&nbsp;35&nbsp;34&nbsp;33&nbsp;32&nbsp;31&nbsp;30&nbsp;39(98765432109的acill码)&nbsp;74(新String)&nbsp;00&nbsp;08(值字面量长度8)

&nbsp;30&nbsp;78&nbsp;46&nbsp;46&nbsp;46&nbsp;46&nbsp;46&nbsp;46(0xFFFFFF的ascil码)

如有错误望指出。

/   使用技巧   /

前面有提到,Serializable的序列化机制里,提供了一些操作入口。在对象侧,有

  • writeObject()

  • readObject()

  • readObjectNoData()

  • writeReplace()

  • readResolve()

以及serialPersistentFields。在解析测,有:

  • writeObjectOverride()

  • replaceObject()

下面是以上一些操作的例子。

serialPersistentFields

对象可以实现serialPersistentFields,用来声明序列化规则,比如,在上面的Phone代码中添加:

&nbsp;private&nbsp;static&nbsp;final&nbsp;ObjectStreamField[]&nbsp;serialPersistentFields&nbsp;=&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new&nbsp;ObjectStreamField("brand",&nbsp;String.class),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new&nbsp;ObjectStreamField("card1",&nbsp;Card.class),
&nbsp;&nbsp;&nbsp;&nbsp;};

再次进行序列化,只有brand和card1的信息被包含了,其他属性的信息没有录入。

serialPersistentFields声明了序列化哪些属性,而transient声明了不序列化哪些属性,这里就不做验证。关于serialPersistentFields的具体代码,可见ObjectSteamClass.getSerialFields() -> ObjectSteamClass.getDeclaredSerialFields()

writeObject

对象可以声明writeObject(),可以属性被序列化之前进行操作。可以在Phone中加入代码

&nbsp;&nbsp;private&nbsp;void&nbsp;writeObject(java.io.ObjectOutputStream&nbsp;steam)&nbsp;throws&nbsp;java.io.IOException{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;咱们的手机打折了
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;price&nbsp;=&nbsp;price&nbsp;*&nbsp;7&nbsp;/&nbsp;10;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;继续使用默认序列化流程
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;steam.defaultWriteObject();
&nbsp;&nbsp;&nbsp;&nbsp;}

原先的代码里,price设置为3000,在序列化前被打了7折,变成了2100,对应的16进制就是 0x00000834。在实际的场景里,这里可以用于对具体数据加密操作。既然有writeObject()做预处理,那么就会有readObject()做解预处理, 这里过程体现在反序列化中,这里就不做例子了。

writeObject代码见 ObjectOutputStraem.writeOrdinaryObject() -> writeSerialData()。

writeReplace

对象可以实现writeReplace(),在获取对象描述前,可对对象进行替换。代码位于ObjectOutputStraem.writeObject0()。而在那段代码中,可以看出两层信息:

可以在这里对对象里的属性值进行修改;可以替换整个对象。先看第一种情况,在Phone中加入代码:

&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;Object&nbsp;writeReplace(){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Card&nbsp;replaceCard&nbsp;=&nbsp;new&nbsp;Card("super&nbsp;card!!!");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;card1&nbsp;=&nbsp;replaceCard;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;this;
&nbsp;&nbsp;&nbsp;&nbsp;}

将card1替换为别的卡,下面是序列化后的二进制流。

card1已经被替换。将刚才的代码更换,看第二种情况:

&nbsp;&nbsp;private&nbsp;Object&nbsp;writeReplace(){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;"all&nbsp;phone&nbsp;sale&nbsp;out";
&nbsp;&nbsp;&nbsp;&nbsp;}

将Phone替换为String,下面是二进制流:

Phone已经被整个替换。这里也可以看出,默认的String是不带serialVersionUID的。

readResolve

readResolve()与writeReplace()类似,也可以替换对象。readResolve()是在反序列化过程中,解析出对象后,对这个对象进行再操作并返回一个新的对象。这里就不做例子。

readObjectNoData

当序列化的过程中,如果这个对象所有的属性都没有值,可以实现readObjectNoData(),以在这种场景下做一些操作,比如设置特殊值,这里不做例子,具体的方法为:

private&nbsp;void&nbsp;readObjectNoData(){
}

replaceObject()

可以自定义ObjectOutputStream()来实现replaceObject(),反序列化过程,对一些对象进行替换,如自定义MyObjectOutputStream,并使用此类替换例子中的ObjectOutputStream。

public&nbsp;class&nbsp;MyObjectOutputStream&nbsp;extends&nbsp;ObjectOutputStream&nbsp;{

&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;MyObjectOutputStream(OutputStream&nbsp;out)&nbsp;throws&nbsp;IOException&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super(out);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;enableReplaceObject(true);
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;@Override
&nbsp;&nbsp;&nbsp;&nbsp;protected&nbsp;Object&nbsp;replaceObject(Object&nbsp;obj)&nbsp;throws&nbsp;IOException&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(obj&nbsp;instanceof&nbsp;Card){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Card&nbsp;curCard&nbsp;=&nbsp;(Card)obj;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curCard.setNumber("857857857");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;super.replaceObject(obj);
&nbsp;&nbsp;&nbsp;&nbsp;}
}

序列化后的信息为:

完成替换。代码位于ObjectOutputStream.writeObject0()

writeObjectOverride

如果对于系统提供的序列化过程不满足,还可以实现writeObjectOverride()来自定义序列化过程。将MyObjectOutputStream的代码替换为:

public&nbsp;class&nbsp;MyObjectOutputStream&nbsp;extends&nbsp;ObjectOutputStream&nbsp;{

&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;MyObjectOutputStream(OutputStream&nbsp;out)&nbsp;throws&nbsp;IOException&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super(out);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;enableOverride&nbsp;是私有的,为了方便这里反射更改
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class<?>&nbsp;parentCl&nbsp;=&nbsp;getClass().getSuperclass();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Field&nbsp;enableOverride&nbsp;=&nbsp;parentCl.getDeclaredField("enableOverride");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;enableOverride.setAccessible(true);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;enableOverride.set(this,&nbsp;true);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(Exception&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;@Override
&nbsp;&nbsp;&nbsp;&nbsp;protected&nbsp;void&nbsp;writeObjectOverride(Object&nbsp;obj)&nbsp;throws&nbsp;IOException&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;模拟自己先序列化过程
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeBytes("I&nbsp;know&nbsp;the&nbsp;Serializable!!");
&nbsp;&nbsp;&nbsp;&nbsp;}
}

完成序列化,代码位于ObjectOutputStream.writeObject()。

/   总结   /

文章到这里就结束了,以writeObjectOverride()结尾,也祝大家在看完后,know the Serializable。文章中并未涉及到反序列化的讲解,而在知道了序列化原理之后,反序列化可见一斑,想要了解反序列化的原理会很轻松,因此不做陈述。对于serialVersionUID的使用也没有做例子验证,大家感兴趣的话,可以自行验证。

/   回答   /

什么是对象序列化

将对象的类型、属性、父类、数据信息,按照一定规则解析成二进制,存储、传输,并能通过反序列化得到有意义的对象。

Serializable如何实现

以对象为根结点,将类型、父类、成员变量视为子节点,遍历这颗树,反射获取信息。并借助ObjectSteamClass、ObjectStreamField、HandleTable记录过程信息。最后将这些信息按约定规则输出二进制流。

Serializable如何存储

存储于二进制流中。可以将这串流视为携带数据的索引表。

Serializable有什么使用技巧

对象侧:

  • writeObject(): 序列化前操作对象数据,比如加密

  • readObject(): 反序列化前操作对象数据,比如揭秘

  • readObjectNoData():对象没有属性数据时,可以提供特殊处理

  • writeReplace():替换序列化的对象

  • readResolve():替换反序列化后的对象

解析侧:

  • writeObjectOverride():自定义序列化过程

  • replaceObject():切面替换要序列化的对象

推荐阅读:

这本《第三行代码》,让大家久等了!

什么?这个天天使用的API竟然被废弃了?

某车App 360度旋转看车效果实现

欢迎关注我的公众号

学习技术或投稿

长按上图,识别图中二维码即可关注