内容简介:java序列化的内部实现(一)
前言
上一遍关于 HashSet 的最后部分提到自定义序列化重写序列化的 readObect 和 writeObject 方法,个人感觉结束比较仓促。由于序列化在 java 中有着举重轻重的地位,尤其是在 RPC 框架中,对象的传输都是通过序列化完成。所以萌生了对 java 的序列化做一次系统的总结。
初步设想关于 java 序列化的总结分成三部分: java 序列化的内部实现、 java 反序列化的内部实现、 java 序列化用法以及理论。
本篇分享为第一部分 “java 序列化的内部实现 ” ,主要是通过一个序列化实例 剖析 java 序列化的内部实现过程。
Java 对象序列化
java 提供了一个“对象序列化框架”,可以将“对象”编码为“字节流”,这个过程称之为 “ 序列化 ” ;反之 也可以从 “ 字节流 ” 编码中重新构建成一个新的对象,这个过程称之为 “ 反序列化 ” 。一旦一个对象被序列化后,就可以通过网络从一台服务器传输到另一台服务器,再进行存储或者反序列化后使用。这个过程在 RPC 框架中大量使用,因此良好的序列化设计可以减少这个过程的开销,提升服务性能。
Java 对象的序列化和反序列化可以通过 ObjectOutputStream 和 ObjectInputStream 实现,首先编写一个简单的对象序列化和反序列化实现,这里模拟将一个 User 对象序列化并存储到 D 盘的 user.txt 文件中,代码如下:
package com.sky.serial;
import java.io.*;
/**
* Created by gantianxing on 2017/5/26.
*/
public class User implements Serializable {
//可以用eclipse生成, 也可以随意指定一个非0的值
private static final long serialVersionUID = 1L;
private final String name;//姓名
private final int sex;//性别0-女 1-男
private String phoneNum;//手机号
public User(String name,int sex){
this.name = name;
this.sex = sex;
}
public String getName() {
return name;
}
public int getSex() {
return sex;
}
public String getPhoneNum() {
return phoneNum;
}
public void setPhoneNum(String phoneNum) {
this.phoneNum = phoneNum;
}
@Override
public String toString(){
return "user info: name="+name+",sex="+sex+",phoneNum="+phoneNum;
}
public static void main(String[] args) throws Exception{
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("D://user.txt"));
//实例化一个user对象
User user = new User("zhang san",1);
user.setPhoneNum("13888888888");
//将对象序列化存储到D:/user.txt 文件中
out.writeObject(user);
ObjectInputStream in = new ObjectInputStream(new FileInputStream("D://user.txt"));
//反序列化
Object newUser = in.readObject();
System.out.println(newUser);
}
}
执行上面代码的 main 方法,控制台打印结果为:
” user info: name=zhang san,sex=1,phoneNum=13888888888” 。
打开 D:/user.txt 文件进行查看,由于我们是将字节流存储到文件中的,所有直接查看 看到的是乱码,所以需要以二进制的格式查看。 java 序列化的的过程中都是以 16 进制写入字节流,采用 16 进制格式查看对阅读 java 序列化的源码很方便(用 UE 和 EditPlus 都可以,我这里使用的 EditPlus )。结果为:
AC ED 00 05 73 72 00 13 63 6F 6D 2E 73 6B 79 2E
73 65 72 69 61 6C 2E 55 73 65 72 00 00 00 00 00
00 00 01 02 00 03 49 00 03 73 65 78 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 4C 00 08 70 68 6F 6E 65
4E 75 6D 71 00 7E 00 01 78 70 00 00 00 01 74 00
09 7A 68 61 6E 67 20 73 61 6E 74 00 0B 31 33 38
38 38 38 38 38 38 38 38
思维先转变过来:这里的每个空格隔开的是 1 个 16 进制位 表示的是一个字节。直接看这一串 16 进制可以能有点晕。通过阅读 java 对象序列化的源码,我把它翻译了一下,每个字节的具体含义如下:
对应上图,我把每一个块儿,再详细描述一次(一共分为28步):
1 、 AC ED 00 05 :在调用 ObjectOutputStream(OutputStream out) 构造方法时生成。具体是在 writeStreamHeader() 方法:
protected void writeStreamHeader() throws IOException {
bout.writeShort(STREAM_MAGIC); //魔法数(Magic number 翻译)
bout.writeShort(STREAM_VERSION);//版本号
}
STREAM_MAGIC 、 STREAM_VERSION 是在 ObjectStreamConstants 中定义的常量:
/**
* Magic number that is written to the stream header.
*/
final static short STREAM_MAGIC = (short)0xaced;
/**
* Version number that is written to the stream header.
*/
final static short STREAM_VERSION = 5;
AC ED 为魔法数。版本号为 short 类型的 5 , short 是两个字节,用两个字节的 16 进制表示 5 即为: 00 05
2 、 73 : TC_OBJECT 在 ObjectStreamConstants 中定义为新对象,表示接下来是一个对象。
/**
* new Object.
*/
final static byte TC_OBJECT = (byte)0x73;
3 、 72 : TC_CLASSDESC 表示接下来是类的描述信息,在 ObjectStreamConstants 中定义为:
/**
* new Class Descriptor.
*/
final static byte TC_CLASSDESC = (byte)0x72;
4 、 00 13 : 表示该对象对应类的全路径类名长度 (com.sky.serial.User 长度为 19) ,这里是 16 进制表示 0x13 ,转换为 10 进制即为 19 。
5 、 63 6F 6D 2E 73 6B 79 2E 73 65 72 69 61 6C 2E 55 73 65 72 :就是用 16 进制表示的字符串 com.sky.serial.User
接下来是成员变量的描述信息:
6 、 00 00 00 00 00 00 00 01 : 这 8 个字节表示的是 long 型的 SUID ,对应的 private static final long serialVersionUID = 1L;
7 、 02 : SC_SERIALIZABLE 表示该类支持序列化,在 ObjectStreamConstants 中定义为:
/**
* Bit mask for ObjectStreamClass flag. Indicates class is Serializable.
*/
final static byte SC_SERIALIZABLE = 0x02;
8 、 00 03 : 表示成员变量个数, user 类中的成员变量个数为 3 ( name 、 sex 、 phoneNum )。
9 、 49 : 表示第一个成员变量( sex )的类型, 16 进制的 49 转换为十进制为 73 ,刚好是大写字母 ’I’ 对应的值。 ’I’ 表示该类型为 int 。
10 、 00 03 : 表示该变量名的长度, “sex” 的长度为 3 。
11 、 73 65 78 :转换成十进制分别为 115 101 120 ,对应的字符分布为 s e x ,即字符串 sex 。
第一个成员的描述信息结束
12 、 4C: 表示第二个成员变量( name )的类型,转换为十进制: 76 ,对应为大写字母 ‘L’ ,表示是一个类。
13 、 00 04 :表示变量名的长度, “name” 的长度为 4
6E 61 6D 65 :转换为十进制分别为 110 97 109 101, 对应的字符拼接起来刚好是字符串 ”name” 。
14 、 74 : TC_STRING 表示对象为 String 类型,在 ObjectStreamConstants 中定义为:
/**
* new String.
*/
final static byte TC_STRING = (byte)0x74;
15 、 00 12 :表示这个类描述信息的长度( "Ljava/lang/String;" ),对应的十进制为 18 。
16 、 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B: 表示的即为字符串 "Ljava/lang/String;"
第二个成员变量描述信息结束
17 、 4C :第三个成员变量( phoneNum ),依然是一个对象(还是为 String )。
18 、 00 08 : ” phoneNum” 的长度为 8 。
19 、 70 68 6F 6E 65 4E 75 6D :对应的值即为字符串 ” phoneNum” 。
20 、 71: TC_REFERENCE 表示是引用,这里 ” phoneNum” 也是 String 类型,前面已经解析过一次 String (第二个成员变量),以后再用直接应用即可。在 ObjectStreamConstants 中定义为:
/**
* Reference to an object already written into the stream.
*/
final static byte TC_REFERENCE = (byte)0x71;
21 、 00 7E 00 01 :新创建对象从 00 7E 00 00 开始, 01 表示引用序号 ( 从 00 到 XX) 这里表示第二个成员变量。在 ObjectStreamConstants 中定义为:
/**
* First wire handle to be assigned.
*/
final static int baseWireHandle = 0x7e0000;
22 、 78 : TC_ENDBLOCKDATA 表是类的描述信息序列化结束,与前面的 72 相对应,在 ObjectStreamConstants 中定义为:
/**
* End of optional block data blocks for an object.
*/
final static byte TC_ENDBLOCKDATA = (byte)0x78;
23 、 70 : TC_NULL 表示已经被引用,即没有父类,如果该类还是父类,下一步继续解析父类的类描述信息。这里我们的 user 没有父类,类描述信息序列化结束。在 ObjectStreamConstants 中定义为:
/**
* Null object reference.
*/
final static byte TC_NULL = (byte)0x70;
接下来开始序列化,成员变量的值信息。
24 、 00 00 00 01 :第一个成员变量 sex 的值,这里为整型值 1 , int 是 4 个字节,即表示为: 00 00 00 01
25 、 74 00 09 :第二成员变量, 74 表示接下来的值为一个 String 对象, 00 09 表示值的长度,这里的值为 “zhang san” 刚好为 9 。
26 、 7A 68 61 6E 67 20 73 61 6E :表示的是第二个成员变量的值 “zhang san” 。
27 、 74 00 0B :第三个成员变量, 74 同样表示接下来的值为一个 String 对象, 00 0B 表示长度是 11 ,这里刚好是 “13888888888” 的长度
28 、 31 33 38 38 38 38 38 38 38 38 38 :表示的是第三个成员变量的值 “13888888888” (逐个字符拼接,比如字符 8 ,对应的 int 值为 56 ,对应的 16 进制即为 0x38 )。
到这里整个序列化流程结束。这只是一个最简单的对象序列化过程,复杂点的比如 user 类多级继承,成员变量还有自定义对象,自定义对象里又有多级继承等等,序列化会解析类的整个拓扑结构。这里就不再对其他复杂的序列化字节文件进行逐一分析,我们可以在下一节阅读源码中,看到各种情况的序列化过程。
简单总结下: java 对象的序列化分成两部分,前一部分是对类以及成员的描述进行序列化(元数据),第二部分是对成员的值进行序列化。
序列化过程源码解析
也许你会问上一节中的字节码是怎么解析出来的,其实一切尽在阅读 java 序列化框架的源码。 Java 序列化框架的核心类包括: ObjectOutputStream 、 ObjectInputStream 、 ObjectStreamClass 、 ObjectStreamField 、 ObjectStreamConstants 。
其中序列化和反序列化的主要业务逻辑都在 ObjectOutputStream 、 ObjectInputStream 中完成, ObjectStreamClass 里主要存放类描述信息, ObjectStreamField 存放成员变量描述信息, ObjectStreamConstants 存放的主要是一些 16 进制的常量(文章最后会列举出主要的常量信息)。
先看下上一节中的序列化代码:
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("D://user.txt"));
//实例化一个user对象
User user = new User("zhang san",1);
user.setPhoneNum("13888888888");
//将对象序列化存储到D:/user.txt 文件中
out.writeObject(user);
第一步创建首一个 ObjectOutputStream 的实例 out ,然后调用 out 的 writeObject() 方法。我们就以这里为入口,开始阅读 java 对象序列化的源码。
1 、 ObjectOutputStream(OutputStream out) 构造方法处理逻辑:
public ObjectOutputStream(OutputStream out) throws IOException {
verifySubclass(); //验证有无子类,这里没有
bout = new BlockDataOutputStream(out);//实例化输出流,通过调用bout.writexxx方法向流中写入内容
handles = new HandleTable(10, (float) 3.00); //初始化handles map,已序列化过的对象成员会放入其中
subs = new ReplaceTable(10, (float) 3.00); //初始化替换对象(替换)map
enableOverride = false;
writeStreamHeader();//写入魔法数、版本 “AC ED 00 05”
bout.setBlockDataMode(true);
if (extendedDebugInfo) {//开启序列化日志-D sun.io.serialization.extendedDebugInfo=true
debugInfoStack = new DebugTraceInfoStack();
} else {
debugInfoStack = null;
}
}
该构造方法,主要初始化工作: a 、实例化输出流对象 bout ,后续调用其 writexxx 方法向流中写入数据; b 、初始化一个内部实现的 handles Map ,后续用于存放已经处理过的对象成的引用; c 、初始化一个内部实现的 subs Map ,后续用于存放 ObjectOutputStream 的受信子类的替换对象(用一个对象替换另外一个对象)
2 、第二步再开来序列化入口方法 writeObject(Object obj)
public final void writeObject(Object obj) throws IOException {
if (enableOverride) {//初始化时是false,跳过
writeObjectOverride(obj);//执行子类的writeObjectOverride方法
return;
}
try {
writeObject0(obj, false);//调用该方法向流 写入对象
} catch (IOException ex) {
if (depth == 0) {
writeFatalException(ex);
}
throw ex;
}
}
这个方法先判断 bout 是否是 ObjectOutputStream 的子类。这我们直接使用 ObjectOutputStream 进行实例化,跳过 if 方法,直接执行 writeObject0 方法。
3 、第三步,调用 writeObject0 方法:
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) { // subs.lookup(obj) 查找是否有替换对象
writeNull(); //写入空对象 TC 70
return;
} else if (!unshared && (h = handles.lookup(obj)) != -1) {// handles.lookup(obj) 查找是否写入,如果已经写入过,直接引用
writeHandle(h);//写入引用TC 71
return;
} else if (obj instanceof Class) {
writeClass((Class) obj, unshared); //写入引用类TC 76
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);//根据cl创建ObjectStreamClass,即把待序列化对象 相关信息写入ObjectStreamClass,
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) {//如果已替换,重新执行一次检查,写入TC
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); //写入String
} 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); //写入序列化对象 TC_OBJECT 73 、类描述信息(类名、suid、序列化、成员个数)
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
} finally {
depth--;
bout.setBlockDataMode(oldMode);
}
}
第一次进入 writeObject0 方法前面验证都会跳过,直到调用 writeOrdinaryObject 方法,开始写入类描述信息
4 、第四步,调用 writeOrdinaryObject 方法 开始写入 User 类描述信息
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);//写入新类TC 73
writeClassDesc(desc, false);//写入类描述信息、成员变量描述信息
handles.assign(unshared ? null : obj);//处理过的类写入handles,以便下次直接引用使用
if (desc.isExternalizable() && !desc.isProxy()) {
writeExternalData((Externalizable) obj);//调用用户重写的writeExternal方法,写入成员变量值
} else {
writeSerialData(obj, desc);//否则调用默认方法 写入成员变量值
}
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
这个方法首先调用 writeClassDesc() 方法向流中写入类描述信息、成员变量描述信息。再调用 writeSerialData() 方法 ( 在步骤 6 中讲解 ) ,写入成员变量值。先看 writeClassDesc()
private void writeClassDesc(ObjectStreamClass desc, boolean unshared)
throws IOException
{
int handle;
if (desc == null) {
writeNull();
} else if (!unshared && (handle = handles.lookup(desc)) != -1) {//handle中已经存在 该类描述信息 直接引用
writeHandle(handle);
} else if (desc.isProxy()) {
writeProxyDesc(desc, unshared);//代理
} else {
writeNonProxyDesc(desc, unshared);//非代理写入
}
}
这里调用 writeNonProxyDesc 方法。
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);
if (cl != null && isCustomSubclass()) {
ReflectUtil.checkPackageAccess(cl);
}
annotateClass(cl);
bout.setBlockDataMode(false);
bout.writeByte(TC_ENDBLOCKDATA); //该类描述信息写入完毕 TC_ENDBLOCKDATA 78;
writeClassDesc(desc.getSuperDesc(), false); //判断是否还有父类,如果有继续写父类描述信息,否则写入空对象TC_NULL 结束
}
该方法先写入类描述开始 TC_CLASSDESC (72) 标记;
然后调用 writeClassDescriptor(ObjectStreamClass desc) 方法 à 调用 ObjectStreamClass 类的 writeNonProxy 方法写入类描述信息,以及成员描述信息;
最后写入类描述完毕 TC_ENDBLOCKDATA 标记 (78), 并判断是否还有父类,如果有,继续递归调用 writeClassDesc 方法写入父类描述信息,否则 写入空对象标记 TC_NULL(70) 。
5 、最终调用的 ObjectStreamClass 类的 writeNonProxy 方法,写入类描述信息:
protected void writeClassDescriptor(ObjectStreamClass desc)
throws IOException
{
desc.writeNonProxy(this);
}
//ObjectStreamClass类的writeNonProxy方法
void writeNonProxy(ObjectOutputStream out) throws IOException {
out.writeUTF(name);
out.writeLong(getSerialVersionUID()); //写入suid,这里是1L, 8个字节表示为: 00 00 00 00 00 00 00 01
byte flags = 0;
if (externalizable) {
flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
int protocol = out.getProtocolVersion();
if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) {
flags |= ObjectStreamConstants.SC_BLOCK_DATA;
}
} else if (serializable) {
flags |= ObjectStreamConstants.SC_SERIALIZABLE;//这里User类实现Serializable,判断逻辑走这里
}
if (hasWriteObjectData) {
flags |= ObjectStreamConstants.SC_WRITE_METHOD;
}
if (isEnum) {
flags |= ObjectStreamConstants.SC_ENUM;
}
out.writeByte(flags); //写入 SC_SERIALIZABLE (02)
out.writeShort(fields.length); //写入成员变量个数3个
for (int i = 0; i < fields.length; i++) {//遍历成员变量sex、name、phoneNum
ObjectStreamField f = fields[i];
out.writeByte(f.getTypeCode()); //写入成员变量类型
out.writeUTF(f.getName()); //写入成员变量名称
if (!f.isPrimitive()) { //如果是对象,写入对象类型,这里name、phoneNum需要写入
out.writeTypeString(f.getTypeString());//写入对象类型
}
}
}
void writeTypeString(String str) throws IOException {
int handle;
if (str == null) {
writeNull();
} else if ((handle = handles.lookup(str)) != -1) {
writeHandle(handle); //phoneNum调用这个方法,直接引用name的String描述即可
} else {
writeString(str, false); //name字段 调用这个方法
}
}
private void writeString(String str, boolean unshared) throws IOException {
handles.assign(unshared ? null : str); //name 字段的类型String 在这一步写入handles Map,后续如果还有String成员,直接引用即可。
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);
}
}
以上是类描述和成员变量描述信息的序列化。
6 、回到步骤 4 ,类描述和成员变量描述信息的序列化完成后,继续调用 writeSerialData 方法写入各个成员变量值信息。
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()) { //判断是否重新了writeObject方法
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);//调用对象重写的writeObject方法
bout.setBlockDataMode(false);
bout.writeByte(TC_ENDBLOCKDATA);
} finally {
curContext.setUsed();
curContext = oldContext;
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
curPut = oldPut;
} else {
defaultWriteFields(obj, slotDesc); //User对象的成员值写入,调用这个默认方法
}
}
}
ObjectStreamClass 类的解析成员拓扑结构方法
private ClassDataSlot[] getClassDataLayout0()
throws InvalidClassException
{
ArrayList<ClassDataSlot> slots = new ArrayList<>();
Class<?> start = cl, end = cl;
// locate closest non-serializable superclass
while (end != null && Serializable.class.isAssignableFrom(end)) {
end = end.getSuperclass();
}
HashSet<String> oscNames = new HashSet<>(3);
for (ObjectStreamClass d = this; d != null; d = d.superDesc) {
if (oscNames.contains(d.name)) {
throw new InvalidClassException("Circular reference.");
} else {
oscNames.add(d.name);
}
// search up inheritance hierarchy for class with matching name
String searchName = (d.cl != null) ? d.cl.getName() : d.name;
Class<?> match = null;
for (Class<?> c = start; c != end; c = c.getSuperclass()) {
if (searchName.equals(c.getName())) {
match = c;
break;
}
}
// add "no data" slot for each unmatched class below match
if (match != null) {
for (Class<?> c = start; c != match; c = c.getSuperclass()) {
slots.add(new ClassDataSlot(
ObjectStreamClass.lookup(c, true), false));
}
start = match.getSuperclass();
}
// record descriptor/class pairing
slots.add(new ClassDataSlot(d.getVariantFor(match), true)); //记录类的继承拓扑关系()
}
// add "no data" slot for any leftover unmatched classes
for (Class<?> c = start; c != end; c = c.getSuperclass()) {
slots.add(new ClassDataSlot(
ObjectStreamClass.lookup(c, true), false)); //记录类的继承拓扑关系
}
// order slots from superclass -> subclass
Collections.reverse(slots);
return slots.toArray(new ClassDataSlot[slots.size()]);
}
7 、 defaultWriteFields 默认的值写入方法
private void defaultWriteFields(Object obj, ObjectStreamClass desc)
throws IOException
{
Class<?> cl = desc.forClass();
if (cl != null && obj != null && !cl.isInstance(obj)) {
throw new ClassCastException();
}
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);//写入基础类型,如:int char long等,这里是写入sex字段对应的值
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()); //递归调用writeObject0方法写入对象,这里是写入String值
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
}
到这里整个序列化过程结束。
ObjectStreamClass 描述类初始化过程
在上一节中步骤 3 中,调用 desc = ObjectStreamClass.lookup(cl, true); 根据 cl 创建 ObjectStreamClass 。简单的说就是把 User 对象相关信息先写入 ObjectStreamClass 中,共后续 bout 写入流使用。
static ObjectStreamClass lookup(Class<?> cl, boolean all) {
if (!(all || Serializable.class.isAssignableFrom(cl))) {
return null;
}
processQueue(Caches.localDescsQueue, Caches.localDescs);
WeakClassKey key = new WeakClassKey(cl, Caches.localDescsQueue); //创建弱引用的key对象
Reference<?> ref = Caches.localDescs.get(key);
Object entry = null;
if (ref != null) {
entry = ref.get();
}
EntryFuture future = null;
if (entry == null) {
EntryFuture newEntry = new EntryFuture();
Reference<?> newRef = new SoftReference<>(newEntry);
do {
if (ref != null) {
Caches.localDescs.remove(key, ref); //如果缓存中已经存在,先移除
}
ref = Caches.localDescs.putIfAbsent(key, newRef);//放入的新类描述到缓存中
if (ref != null) {
entry = ref.get();
}
} while (ref != null && entry == null);
if (entry == null) {
future = newEntry;
}
}
if (entry instanceof ObjectStreamClass) { // check common case first
return (ObjectStreamClass) entry;
}
if (entry instanceof EntryFuture) {
future = (EntryFuture) entry;
if (future.getOwner() == Thread.currentThread()) {
/*
* Handle nested call situation described by 4803747: waiting
* for future value to be set by a lookup() call further up the
* stack will result in deadlock, so calculate and set the
* future value here instead.
*/
entry = null;
} else {
entry = future.get();
}
}
if (entry == null) {
try {
entry = new ObjectStreamClass(cl); //调用该构造方法进行初始化
} catch (Throwable th) {
entry = th;
}
if (future.set(entry)) {
Caches.localDescs.put(key, new SoftReference<Object>(entry)); //类描述信息放入缓存
} else {
// nested lookup call already set future
entry = future.get();
}
}
if (entry instanceof ObjectStreamClass) {
return (ObjectStreamClass) entry;
} else if (entry instanceof RuntimeException) {
throw (RuntimeException) entry;
} else if (entry instanceof Error) {
throw (Error) entry;
} else {
throw new InternalError("unexpected entry: " + entry);
}
}
//类描述初始化
private ObjectStreamClass(final Class<?> cl) {
this.cl = cl;
name = cl.getName(); //对象类名
isProxy = Proxy.isProxyClass(cl); //是否是代理类
isEnum = Enum.class.isAssignableFrom(cl); //是否是枚举
serializable = Serializable.class.isAssignableFrom(cl); //是否实现了序列化接口
externalizable = Externalizable.class.isAssignableFrom(cl);//是否实现了externalizable接口
Class<?> superCl = cl.getSuperclass();
superDesc = (superCl != null) ? lookup(superCl, false) : null; //直接父类,Object不算
localDesc = this;
if (serializable) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
if (isEnum) {
suid = Long.valueOf(0);
fields = NO_FIELDS;
return null;
}
if (cl.isArray()) {
fields = NO_FIELDS;
return null;
}
suid = getDeclaredSUID(cl); //获取对象的suid
try {
fields = getSerialFields(cl);
computeFieldOffsets();
} catch (InvalidClassException e) {
serializeEx = deserializeEx =
new ExceptionInfo(e.classname, e.getMessage());
fields = NO_FIELDS;
}
if (externalizable) { //是否实现Externalizable接口
cons = getExternalizableConstructor(cl);
} else {
cons = getSerializableConstructor(cl);
writeObjectMethod = getPrivateMethod(cl, "writeObject",
new Class<?>[] { ObjectOutputStream.class },
Void.TYPE); //获取重写的writeObject方法
readObjectMethod = getPrivateMethod(cl, "readObject",
new Class<?>[] { ObjectInputStream.class },
Void.TYPE);//获取重写的readObject方法
readObjectNoDataMethod = getPrivateMethod(
cl, "readObjectNoData", null, Void.TYPE);//获取重写的readObjectNoData方法
hasWriteObjectData = (writeObjectMethod != null);
}
writeReplaceMethod = getInheritableMethod(
cl, "writeReplace", null, Object.class);//获取重写的writeReplace方法
readResolveMethod = getInheritableMethod(
cl, "readResolve", null, Object.class);//获取重写的readResolve方法
return null;
}
});
} else {
suid = Long.valueOf(0);
fields = NO_FIELDS;
}
try {
fieldRefl = getReflector(fields, this); //初始化成员变量描述信息
} catch (InvalidClassException ex) {
// field mismatches impossible when matching local fields vs. self
throw new InternalError(ex);
}
if (deserializeEx == null) {
if (isEnum) {
deserializeEx = new ExceptionInfo(name, "enum type");
} else if (cons == null) {
deserializeEx = new ExceptionInfo(name, "no valid constructor");
}
}
for (int i = 0; i < fields.length; i++) {
if (fields[i].getField() == null) {
defaultSerializeEx = new ExceptionInfo(
name, "unmatched serializable field(s) declared");
}
}
initialized = true;
}
//获取对象的成员变量列表
private static ObjectStreamField[] getSerialFields(Class<?> cl)
throws InvalidClassException
{
ObjectStreamField[] fields;
if (Serializable.class.isAssignableFrom(cl) &&
!Externalizable.class.isAssignableFrom(cl) &&
!Proxy.isProxyClass(cl) &&
!cl.isInterface())
{
if ((fields = getDeclaredSerialFields(cl)) == null) {
fields = getDefaultSerialFields(cl);
}
Arrays.sort(fields); //排序,这就是为什么按照sex、name、phoneNum进行排序
} else {
fields = NO_FIELDS;
}
return fields;
}
//初始化成员变量描述信息,
private static FieldReflector getReflector(ObjectStreamField[] fields,
ObjectStreamClass localDesc)
throws InvalidClassException
{
// class irrelevant if no fields
Class<?> cl = (localDesc != null && fields.length > 0) ?
localDesc.cl : null;
processQueue(Caches.reflectorsQueue, Caches.reflectors);
FieldReflectorKey key = new FieldReflectorKey(cl, fields,
Caches.reflectorsQueue);
Reference<?> ref = Caches.reflectors.get(key);
Object entry = null;
if (ref != null) {
entry = ref.get();
}
EntryFuture future = null;
if (entry == null) {
EntryFuture newEntry = new EntryFuture();
Reference<?> newRef = new SoftReference<>(newEntry);
do {
if (ref != null) {
Caches.reflectors.remove(key, ref);
}
ref = Caches.reflectors.putIfAbsent(key, newRef);//重新缓存
if (ref != null) {
entry = ref.get();
}
} while (ref != null && entry == null);
if (entry == null) {
future = newEntry;
}
}
if (entry instanceof FieldReflector) { // check common case first
return (FieldReflector) entry;
} else if (entry instanceof EntryFuture) {
entry = ((EntryFuture) entry).get();
} else if (entry == null) {
try {
entry = new FieldReflector(matchFields(fields, localDesc)); //调用FieldReflector构造方法进行初始化
} catch (Throwable th) {
entry = th;
}
future.set(entry);
Caches.reflectors.put(key, new SoftReference<Object>(entry));//成员表里描述信息放入缓存
}
if (entry instanceof FieldReflector) {
return (FieldReflector) entry;
} else if (entry instanceof InvalidClassException) {
throw (InvalidClassException) entry;
} else if (entry instanceof RuntimeException) {
throw (RuntimeException) entry;
} else if (entry instanceof Error) {
throw (Error) entry;
} else {
throw new InternalError("unexpected entry: " + entry);
}
}
FieldReflector 是 ObjectStreamClass 的内部静态类,上一步中循环调用 matchFields 方法对 ObjectStreamField 初始化:
private static ObjectStreamField[] matchFields(ObjectStreamField[] fields,
ObjectStreamClass localDesc)
throws InvalidClassException
{
ObjectStreamField[] localFields = (localDesc != null) ?
localDesc.fields : NO_FIELDS;
/*
* Even if fields == localFields, we cannot simply return localFields
* here. In previous implementations of serialization,
* ObjectStreamField.getType() returned Object.class if the
* ObjectStreamField represented a non-primitive field and belonged to
* a non-local class descriptor. To preserve this (questionable)
* behavior, the ObjectStreamField instances returned by matchFields
* cannot report non-primitive types other than Object.class; hence
* localFields cannot be returned directly.
*/
ObjectStreamField[] matches = new ObjectStreamField[fields.length];
for (int i = 0; i < fields.length; i++) {
ObjectStreamField f = fields[i], m = null;
for (int j = 0; j < localFields.length; j++) {
ObjectStreamField lf = localFields[j];
if (f.getName().equals(lf.getName())) {
if ((f.isPrimitive() || lf.isPrimitive()) &&
f.getTypeCode() != lf.getTypeCode())
{
throw new InvalidClassException(localDesc.name,
"incompatible types for field " + f.getName());
}
if (lf.getField() != null) {
m = new ObjectStreamField(
lf.getField(), lf.isUnshared(), false); //调用ObjectStreamField的构造方法进行初始化
} else {
m = new ObjectStreamField(
lf.getName(), lf.getSignature(), lf.isUnshared());
}
}
}
if (m == null) {
m = new ObjectStreamField(
f.getName(), f.getSignature(), false);
}
m.setOffset(f.getOffset());
matches[i] = m;
}
return matches;
}
// ObjectStreamField构造方法
ObjectStreamField(Field field, boolean unshared, boolean showType) {
this.field = field;
this.unshared = unshared;
name = field.getName();
Class<?> ftype = field.getType();
type = (showType || ftype.isPrimitive()) ? ftype : Object.class;
signature = getClassSignature(ftype).intern();
}
关于常量
需要说下 ObjectStreamConstants 常量类,里面记录有 java 序列化的各种标记,多熟悉这里的常量,对阅读源码很有帮助。这里就不再列举,可以自行查阅。
关于成员变量的标记在 ObjectStreamField 的 getClassSignature 中定义的:
private static String getClassSignature(Class<?> cl) {
StringBuilder sbuf = new StringBuilder();
while (cl.isArray()) {
sbuf.append('['); //数组类型
cl = cl.getComponentType();
}
if (cl.isPrimitive()) { //基础类型
if (cl == Integer.TYPE) {
sbuf.append('I');
} else if (cl == Byte.TYPE) {
sbuf.append('B');
} else if (cl == Long.TYPE) {
sbuf.append('J');
} else if (cl == Float.TYPE) {
sbuf.append('F');
} else if (cl == Double.TYPE) {
sbuf.append('D');
} else if (cl == Short.TYPE) {
sbuf.append('S');
} else if (cl == Character.TYPE) {
sbuf.append('C');
} else if (cl == Boolean.TYPE) {
sbuf.append('Z');
} else if (cl == Void.TYPE) {
sbuf.append('V');
} else {
throw new InternalError();
}
} else {
sbuf.append('L' + cl.getName().replace('.', '/') + ';'); //对象类型
}
return sbuf.toString();
}
保护的构造方法中也有体现:
ObjectStreamField(String name, String signature, boolean unshared) {
if (name == null) {
throw new NullPointerException();
}
this.name = name;
this.signature = signature.intern();
this.unshared = unshared;
field = null;
switch (signature.charAt(0)) {
case 'Z': type = Boolean.TYPE; break;
case 'B': type = Byte.TYPE; break;
case 'C': type = Character.TYPE; break;
case 'S': type = Short.TYPE; break;
case 'I': type = Integer.TYPE; break;
case 'J': type = Long.TYPE; break;
case 'F': type = Float.TYPE; break;
case 'D': type = Double.TYPE; break;
case 'L':
case '[': type = Object.class; break;
default: throw new IllegalArgumentException("illegal signature");
}
}
成员变量的 type 和 signature 互转的逻辑,也在上面两个方法中。
关于 ObjectOutputStream 中的引用重用
ObjectOutputStream 中有两个静态内部类 HandleTable 、 ReplaceTable ,是两个简单的 map 实现。对应的成员变量为:
private final HandleTable handles; private final ReplaceTable subs;
分别用于存放已经序列化过的类描述对象引用、替换类描述对象引用。以便再后续序列化中,遇到相同类型直接引用即可,不再重复解析。
关于序列化对象替换:
新建一个类继承 ObjectOutputStream ,重写其 replaceObject 方法。看一个例子就明白了:
package com.sky.serial;
import java.io.*;
/**
* Created by gantianxing on 2017/5/28.
*/
public class ObjectOutputStreamDemo extends ObjectOutputStream {
public ObjectOutputStreamDemo(OutputStream out) throws IOException {
super(out);
}
@Override
public Object replaceObject(Object obj) throws IOException {
return "replace";
}
public static void main(String[] args) {
Object s1 = "string1";
Object s2 = "string2";
try {
// create a new file with an ObjectOutputStream
FileOutputStream out = new FileOutputStream("D://ss.txt");
ObjectOutputStreamDemo oout = new ObjectOutputStreamDemo(out);
// 序列化s1
oout.writeObject(s1);
// 开启允许替换
oout.enableReplaceObject(true);
// 替换
oout.replaceObject(s2);
//写入S2,这时S2会被"replace"
oout.writeObject(s2);
// close the stream
oout.close();
// create an ObjectInputStream for the file we created before
ObjectInputStream ois =
new ObjectInputStream(new FileInputStream("D://ss.txt"));
// read and print an int
System.out.println("" + (String) ois.readObject());
System.out.println("" + (String) ois.readObject());
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
打印信息为:
string1 replace
说明“string2”在序列化时被“replace”替换。其实上面的源码解析中,也有涉及这部分内容。不再进行详细解析。
关于 ObjectStreamClass 类的缓存
ObjectStreamClass 中一个静态的缓存内部类 Caches ,主要用途是在序列化的过程中尽量使用,已经存在的类描述对象和成员描述对象,减少内存消耗。
private static class Caches {
/** cache mapping local classes -> descriptors */
static final ConcurrentMap<WeakClassKey,Reference<?>> localDescs =
new ConcurrentHashMap<>();
/** cache mapping field group/local desc pairs -> field reflectors */
static final ConcurrentMap<FieldReflectorKey,Reference<?>> reflectors =
new ConcurrentHashMap<>();
/** queue for WeakReferences to local classes */
private static final ReferenceQueue<Class<?>> localDescsQueue =
new ReferenceQueue<>();
/** queue for WeakReferences to field reflectors keys */
private static final ReferenceQueue<Class<?>> reflectorsQueue =
new ReferenceQueue<>();
}
localDescs 、 localDescsQueue 对应的是类描述的缓存;
reflectors 、 reflectorsQueue 对应的是成员描述的缓存;
localDescs 、 reflectors 的 key 是弱引用 WeakReference , value 是软引用 SoftReference
软弱引用都需要配合引用队列使用 ReferenceQueue 。
关于软、弱引入,引用队列这里就不展开讲啦,后面有时间再单独进行总结。
java基础类型的序列化比较简单,直接调用out.writexxx方法即可(比如out.writeInt(x))。其实 java 对象的序列化,本质上也是转化为基础类型的序列化,但为了表示对象的内部关系加了很多TC标识而已。
这次就到这里吧 :-D
下一篇讲解《 java 反序列化的内部实现(二) 》
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。