1.概述
1.1.序列化框架
对象的引用关系,组成了一个图结构,对象即节点,对象之间的引用关系则为边.
对象序列化,就是通过一定的算法,把对象图转化成字节序列,用于网络中传输.
对象序列化框架通常需要具备3个能力:
-
针对对象的继承和组合关系,制定合理的序列化算法
- 具备处理对象图中,对同一对象多次引用的能力
- 可以支持用户自主选择要序列化的数据集合
1.2.JDK的对象序列化
JDK中主要有3个类:
- ObjectOutputStream:负责序列化一个对象
- ObjectInputStream:负责读取字节流,反序列成对象
- ObjectStreamClass:用于描述一个Class对象结构如何被序列化和反序列化
下面重点讲解ObjectStreamClass类型序列化描述符的作用,以及ObjectOutputStream的具体序列化算法的实现.
ObjectInputStream的反序列化逻辑,可以理解为是一个逆向操作,不再赘述.
2.序列化规范
2.1.类型序列化描述符(ObjectStreamClass)详解
字段定义如下:
/**
* 类对象
*/
private Class<?> cl;
/**
* 全限定类名
*/
private String name;
/**
* 是否jdk代理类
*/
private boolean isProxy;
/**
* 是否枚举类
*/
private boolean isEnum;
/**
* 类继承体系中,是否存在某个类实现了Serializable接口
*/
private boolean serializable;
/**
* 类继承体系中,是否存在某个类实现了Externalizable接口
*/
private boolean externalizable;
/**
* 当前类描述符,即this
*/
private ObjectStreamClass localDesc;
/**
* 直接父类描述符
*/
private ObjectStreamClass superDesc;
/**
* 定义序列化格式的版本
* 对应private static final serialVersionUID字段值
* 如果不声明,则会通过默认逻辑计算
*/
private volatile Long suid;
/**
* 可序列化字段
* 可以通过 private static final ObjectStreamField[] serialPersistentFields;进行主动声明
* 也可以走默认反射逻辑,筛选所有声明的非static且非transient的字段
*/
private ObjectStreamField[] fields;
/**
* 基本类型字段占用的字节数
*/
private int primDataSize;
/**
* 引用类型字段的个数
*/
private int numObjFields;
/**
* 反序列化时使用的构造方法
*/
private Constructor<?> cons;
/**
* private void writeObject(ObjectOutputStream)方法,必须非static
* 显示定义的序列化逻,如果不定义,则执行默认的字段序列化逻辑
*/
private Method writeObjectMethod;
/**
* private void readObject(ObjectInputStream)方法,必须非static
*/
private Method readObjectMethod;
/**
* private void readObjectNoData()方法,必须非static
*/
private Method readObjectNoDataMethod;
/**
* 是否存在
* writeObject(ObjectOutputStream)方法
*/
private boolean hasWriteObjectData;
/**
* Object writeReplace()
* 当对象要被序列化时,它可以返回一个新的对象,用于序列化
* 方法必须是具体的实例方法
* 针对访问权限有特殊要求:
* 当前类和父类都允许public或protected,
* 另外额外允许当前类为private的
* 或者方法为默认包权限
*/
private Method writeReplaceMethod;
/**
* Object readResolve()
*/
private Method readResolveMethod;
/**
* 字段反射器,用来读写可序列化字段的值
* 包括基本类型值和引用对象值
*/
private FieldReflector fieldRefl;
/**
* 反序列化异常信息
*/
private ExceptionInfo deserializeEx;
/**
* 序列化异常信息
*/
private ExceptionInfo serializeEx;
/**
* 默认序列化异常
*/
private ExceptionInfo defaultSerializeEx;
// -----------------------------------------------------------------------------------------------------------------
/**
* true if desc has externalizable data written in block data format; this
* must be true by default to accommodate ObjectInputStream subclasses which
* override readClassDescriptor() to return class descriptors obtained from
* ObjectStreamClass.lookup() (see 4461737)
*/
private boolean hasBlockExternalData = true;
/**
* exception (if any) thrown while attempting to resolve class
*/
private ClassNotFoundException resolveEx;
/**
* 数据布局
* 类数据槽数组,序列化对象的数据布局,被当前类描述
*/
private volatile ClassDataSlot[] dataLayout;
构造方法逻辑如下:
/**
* 构造方法
* 创建本地类描述符
*
* @param cl 指定的类对象
*/
private ObjectStreamClass(final Class<?> cl) {
this.cl = cl;
name = cl.getName();
// 判断是否jdk代理(Proxy的子类,且Proxy内部缓存包含这个类对象)
isProxy = Proxy.isProxyClass(cl);
isEnum = Enum.class.isAssignableFrom(cl);
serializable = Serializable.class.isAssignableFrom(cl);
externalizable = Externalizable.class.isAssignableFrom(cl);
// 直接父类
Class<?> superCl = cl.getSuperclass();
// 递归获取父类描述符
superDesc = (superCl != null) ? lookup(superCl, false) : null;
localDesc = this;
// 实现了Serializable接口
if (serializable) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
// 枚举类序列化唯一id为0,没有字段
if (isEnum) {
suid = 0L;
fields = NO_FIELDS;
return null;
}
// 数组类没有字段
if (cl.isArray()) {
fields = NO_FIELDS;
return null;
}
// 获取声明的serialVersionUID
// 可能为null,为null时在调用getter时自动计算默认值保存
suid = getDeclaredSUID(cl);
try {
// 获取序列化字段
// private static final ObjectStreamField[] serialPersistentFields
fields = getSerialFields(cl);
// 计算字段偏移量
computeFieldOffsets();
} catch (InvalidClassException e) {
serializeEx = deserializeEx = new ExceptionInfo(e.classname, e.getMessage());
fields = NO_FIELDS;
}
// 实现了Externalizable接口
if (externalizable) {
// 获取无参构造方法
cons = getExternalizableConstructor(cl);
}
// 没有实现
else {
// 获取可序列化的构造方法???
cons = getSerializableConstructor(cl);
// 获取 private void writeObject(ObjectOutputStream)方法,必须非static
writeObjectMethod = getPrivateMethod(cl,
"writeObject",
new Class<?>[]{ObjectOutputStream.class},
Void.TYPE
);
// 获取private void readObject(ObjectInputStream)方法,必须非static
readObjectMethod = getPrivateMethod(cl,
"readObject",
new Class<?>[]{ObjectInputStream.class},
Void.TYPE
);
// private void readObjectNoData()方法,必须非static
readObjectNoDataMethod = getPrivateMethod(
cl,
"readObjectNoData",
null,
Void.TYPE
);
//
hasWriteObjectData = (writeObjectMethod != null);
}
// 可继承的Object writeReplace(),递归查找父类
writeReplaceMethod = getInheritableMethod(cl, "writeReplace", null, Object.class);
// 可继承的Object readResolve(),递归查找父类
readResolveMethod = getInheritableMethod(cl, "readResolve", null, Object.class);
return null;
}
});
}
// 没有实现Serializable接口,则序列化id默认为0
else {
suid = 0L;
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");
}
}
}
2.2.对象替换机制
ObjectOutputStream支持在序列化之前,由用户替换要序列化的目标对象,需要在原始序列化类体系中,存在一个子类可见的writeReplace()方法.
在反序列化时,则通过readResolve()方法,将对象转换回来.
举例如下:
class User implements Serializable {
private Object writeReplace() {
return this;
}
private Object readResolve() {
return this;
}
}
或者可以这样写:
子类通过继承,同样存在writeReplace()/readResolve()方法.
class BaseEntity implements Serializable {
protected Object writeReplace() {
return this;
}
protected Object readResolve() {
return this;
}
}
class User extends BaseEntity {
}
2.3.特殊对象的序列化
2.3.1.ObjectStreamClass对象
void writeNonProxy(ObjectOutputStream out) throws IOException {
// 类名
out.writeUTF(name);
// 序列化id
out.writeLong(getSerialVersionUID());
byte flags = 0;
// 实现了Externalizable接口
if (externalizable) {
flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
int protocol = out.getProtocolVersion();
// 块数据标记
if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) {
flags |= ObjectStreamConstants.SC_BLOCK_DATA;
}
}
// 实现了Serializable接口
else if (serializable) {
flags |= ObjectStreamConstants.SC_SERIALIZABLE;
}
// 自主定义了writeObject()方法
if (hasWriteObjectData) {
flags |= ObjectStreamConstants.SC_WRITE_METHOD;
}
// 是枚举
if (isEnum) {
flags |= ObjectStreamConstants.SC_ENUM;
}
// 标记
out.writeByte(flags);
// 字段个数
out.writeShort(fields.length);
// 遍历字段描述符
for (int i = 0; i < fields.length; i++) {
ObjectStreamField f = fields[i];
// 类型码
out.writeByte(f.getTypeCode());
// 字段名
out.writeUTF(f.getName());
// 引用类型
if (!f.isPrimitive()) {
out.writeTypeString(f.getTypeString());
}
}
}
2.3.2.String对象
在序列化时,会把String对象进行UTF-8编码,获取字节序列,
之后先写入字节序列的长度,再写入字节序列本身.
当字节序列长度<=65535时,用short表示(占用2字节),否则用long表示(占用8字节).
2.3.3.数组对象
数组对象相比普通对象,需要多序列数组类自身的描述符,以及数组的长度.
private void writeArray(Object array, ObjectStreamClass desc, boolean unshared) throws IOException {
bout.writeByte(TC_ARRAY);
// 写数组描述符
writeClassDesc(desc, false);
// 缓存引用
handles.assign(unshared ? null : array);
// 数组元素的类型
Class<?> ccl = desc.forClass().getComponentType();
// 基本类型
if (ccl.isPrimitive()) {
// int
if (ccl == Integer.TYPE) {
int[] ia = (int[]) array;
// 数组长度
bout.writeInt(ia.length);
// 数组元素值
bout.writeInts(ia, 0, ia.length);
} else if (ccl == Byte.TYPE) {
byte[] ba = (byte[]) array;
bout.writeInt(ba.length);
bout.write(ba, 0, ba.length, true);
} else if (ccl == Long.TYPE) {
long[] ja = (long[]) array;
bout.writeInt(ja.length);
bout.writeLongs(ja, 0, ja.length);
} else if (ccl == Float.TYPE) {
float[] fa = (float[]) array;
bout.writeInt(fa.length);
bout.writeFloats(fa, 0, fa.length);
} else if (ccl == Double.TYPE) {
double[] da = (double[]) array;
bout.writeInt(da.length);
bout.writeDoubles(da, 0, da.length);
} else if (ccl == Short.TYPE) {
short[] sa = (short[]) array;
bout.writeInt(sa.length);
bout.writeShorts(sa, 0, sa.length);
} else if (ccl == Character.TYPE) {
char[] ca = (char[]) array;
bout.writeInt(ca.length);
bout.writeChars(ca, 0, ca.length);
} else if (ccl == Boolean.TYPE) {
boolean[] za = (boolean[]) array;
bout.writeInt(za.length);
bout.writeBooleans(za, 0, za.length);
} else {
throw new InternalError();
}
}
// 引用类型
else {
Object[] objs = (Object[]) array;
int len = objs.length;
// 数组长度
bout.writeInt(len);
if (extendedDebugInfo) {
debugInfoStack.push(
"array (class \"" + array.getClass().getName() +
"\", size: " + len + ")");
}
try {
for (int i = 0; i < len; i++) {
if (extendedDebugInfo) {
debugInfoStack.push("element of array (index: " + i + ")");
}
try {
writeObject0(objs[i], false);
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
}
2.3.4.枚举对象
枚举对象,需要首先序列化枚举类对应的ObjectStreamClass描述符,之后把枚举值的name按照String方式进行序列化.
private void writeEnum(Enum<?> en, ObjectStreamClass desc, boolean unshared) throws IOException {
bout.writeByte(TC_ENUM);
// 获取枚举类的父类描述符
ObjectStreamClass sdesc = desc.getSuperDesc();
writeClassDesc((sdesc.forClass() == Enum.class) ? desc : sdesc, false);
// 缓存对象
handles.assign(unshared ? null : en);
// 以枚举名称序列化
writeString(en.name(), false);
}
2.3.5.Class对象(JDK代理和普通类)
Class对象,把它对应的ObjectStreamClass描述符序列化即可.
但是,需要针对JDK的Proxy类生成的动态代理类进行特殊的序列化处理.
Proxy生成的动态代理类,只需要序列化其代理接口的全限定名称即可.
2.4.普通对象序列化
2.4.1.外部序列化(针对实例整体)
框架允许用户自主定义实例的序列化逻辑,只需要实例对应的类体系中,实现Externalizable接口即可.
比如,我们可以使用JSON格式进行对象的序列化,如下:
public class ExternalizableObject implements Externalizable {
private String data;
/**
* 序列化使用的构造方法
*/
public ExternalizableObject() {
}
public ExternalizableObject(String data) {
this.data = data;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
@Override
public String toString() {
return "ExternalizableObject{" +
"data='" + data + '\'' +
'}';
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
String json = JSON.toJSONString(this);
out.writeUTF(json);
}
@Override
public void readExternal(ObjectInput in) throws IOException {
String json = in.readUTF();
ExternalizableObject obj = JSON.parseObject(json, getClass());
this.data = obj.data;
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
PipedOutputStream out = new PipedOutputStream();
PipedInputStream in = new PipedInputStream();
out.connect(in);
try (ObjectOutputStream objOut = new ObjectOutputStream(out)) {
objOut.writeObject(new ExternalizableObject("你好"));
}
try (ObjectInputStream objIn = new ObjectInputStream(in)) {
Object obj = objIn.readObject();
System.out.println(obj);
}
}
}
2.4.2.内部序列化(针对某一类层级)
2.4.2.1.概述
如果只实现Serializable接口,而不实现Eternalizable接口,则框架会使用内部的序列化机制.
按照类的继承关系,由父类及子类,逐层进行序列化.
每层序列化时,优先识别类中定义的writeObject()方法,如果不存在,则通过反射识别要序列化的字段集合.
2.4.2.2.自定义当前层级的序列化
用户可通过定义如下格式的方法,自行实现当前层级的序列化逻辑.
注意:方法一定要是private的,因为这个方法不允许继承,框架会调用每一层的私有方法.
private void writeObject(ObjectOutputStream out);
private void readObject(ObjectInputStream in);
2.4.2.3.默认序列化机制
如果某一类层级没有定义writeObject()/readObject()方法,则框架会收集需要序列化的字段集合.
优先识别类中定义的serialPersistentFields字段,具体定时方式如下:
private static final ObjectStreamField[] serialPersistentFields;
如果没有声明该字段,则通过反射的方式,筛选当前类中声明的所有非static且非transient的字段,作为序列化字段集合.
字段列表是有序的,采用基本类型优先,其次按照字段名称字典序排列.
2.5.对象图中相同引用的处理
ObjectOutputStream中,有一个叫做处理表(HandleTable)的数据结构;
private final HandleTable handles;
HandleTable是一个简易的哈希表实现.
ObjectOutputStream每序列化一个对象之前,都通过HandleTable.lookup(obj)查找该对象是否已经被序列化过,如果找到则序列化该对象在objs数组中的索引位置,而不是序列化对象本身,
序列化之后,通过HandleTable.assign(obj)保存对象引用.
以这种方式避免相同引用的重复序列化,来保证反序列化时,维护正确的对象引用关系.
private static class HandleTable {
/**
* 元素个数
*/
private int size;
/**
* 阈值,达到阈值触发扩容
*/
private int threshold;
/**
* 负载因子
*/
private final float loadFactor;
/**
* 哈希表,默认-1填充
* 元素值不是-1,则代表链表头元素对应的对象在objs数组中的索引编号
*/
private int[] spine;
/**
* 对象的next指针,
* next[i]代表objs[i]的后继元素是objs[next[i]]
*/
private int[] next;
/**
* 保存所有对象引用
*/
private Object[] objs;
/**
* 构造方法
* 创建新的处理表使用给定的容量和负载因子
*
* @param initialCapacity 初始容量
* @param loadFactor 负载因子
*/
HandleTable(int initialCapacity, float loadFactor) {
this.loadFactor = loadFactor;
spine = new int[initialCapacity];
next = new int[initialCapacity];
objs = new Object[initialCapacity];
threshold = (int) (initialCapacity * loadFactor);
clear();
}
/**
* 保存指定的对象引用
* @param obj 指定的对象引用
* @return
*/
int assign(Object obj) {
// 对象存储扩容
if (size >= next.length) {
growEntries();
}
// 哈希表扩容
if (size >= threshold) {
// spine扩容
growSpine();
}
// 插入对象
insert(obj, size);
return size++;
}
/**
* 查找指定的对象
*
* @param obj 指定的对象
* @return 对象在objs中的位置索引
*/
int lookup(Object obj) {
if (size == 0) {
return -1;
}
// hash slot
int index = hash(obj) % spine.length;
// 遍历链表,查找指定对象引用
for (int i = spine[index]; i >= 0; i = next[i]) {
// 引用相同,则返回位置索引
if (objs[i] == obj) {
return i;
}
}
return -1;
}
void clear() {
Arrays.fill(spine, -1);
Arrays.fill(objs, 0, size, null);
size = 0;
}
int size() {
return size;
}
private void insert(Object obj, int handle) {
int index = hash(obj) % spine.length;
objs[handle] = obj;
next[handle] = spine[index];
spine[index] = handle;
}
private void growSpine() {
spine = new int[(spine.length << 1) + 1];
threshold = (int) (spine.length * loadFactor);
Arrays.fill(spine, -1);
for (int i = 0; i < size; i++) {
insert(objs[i], i);
}
}
private void growEntries() {
int newLength = (next.length << 1) + 1;
int[] newNext = new int[newLength];
System.arraycopy(next, 0, newNext, 0, size);
next = newNext;
Object[] newObjs = new Object[newLength];
System.arraycopy(objs, 0, newObjs, 0, size);
objs = newObjs;
}
private int hash(Object obj) {
return System.identityHashCode(obj) & 0x7FFFFFFF;
}
}