反序列化基础
2024-09-13 09:14:17 # javasec # basic

序列化与反序列化

0x1 什么是反序列化

序列化:对象 -> 字符串
反序列化:字符串 -> 对象

0x2 为什么要反序列化

  • 传递数据,将对象序列化成一段流,用于传输
  • 用于进程之间的通讯

0x3 序列化的优点

  • 数据持久化 ,可以将对象的数据永久存储在硬盘中,即将对象序列化成文件
  • 实现远程通信,在网络上传输对象(传输的是对象的字节流)

0x4 序列化场景

  • 将对象持久化保存在硬盘中,即序列化到文件或数据库中
  • 网络中使用套接字socket传输对象
  • 通过RMI协议传输对象

0x5 序列化和反序列化协议

  • XML SOAP
  • JSON
  • Protobuf

Serializable 接口

Serializable的基本使用

序列化类的属性没有实现 Serializable 那么在序列化就会报错

只有实现 了Serializable 或者 Externalizable 接口的类的对象才能被序列化为字节序列。(不是则会抛出异常)
Serializable 接口是 Java 提供的序列化接口,它是一个空接口,所以其实我们不需要实现什么。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package Serialize;

import java.io.*;

class Person implements Serializable {
public Person(String name, int age) {
this.name = name;
this.age = age;
}

private String name;
public int age;
}

public class Ser {
public static void main(String[] args) throws IOException {
Person person = new Person("pysnow", 20);
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
ObjectOutputStream in = new ObjectOutputStream(bytes);
in.writeObject(person);

System.out.printf(bytes.toString());
}
}

如果去掉这个接口则会报错

Serializable特点1: 父类没实现Serializable接口,子类实现了,那么子类也是可以被序列化的

见特点2

Serializable特点2:为继承接口的父类调用无参构造器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package Serialize;

import java.io.*;

class Person {

public Person() {
System.out.println("父类无参数构造器");
}
public Person(String name, int age) {
System.out.println("父类有参数构造器");
this.name = name;
this.age = age;

}

private String name;
public int age;

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

class Student extends Person implements Serializable {
private int grade;
public String school_name;

public Student(String name, int age, int grade, String school_name) {
super(name, age);
this.grade = grade;
this.school_name = school_name;
}

@Override
public String toString() {
return "Student{" +
"grade=" + grade +
", school_name='" + school_name + '\'' +
'}';
}
}

public class Ser {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Student std = new Student("pysnow", 20,3,"SWPU");
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bytes);
System.out.println("序列化");
out.writeObject(std);

ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray()));
System.out.println("反序列化");
Student stdd = (Student) in.readObject();


System.out.println(stdd.toString());
}
}

简单讲解一下这串代码,定义了两个类Person,Student,学生类继承的Person并且使用了Serializable接口,而父类Person则没有使用Serializable接口。则Student类可以序列化,而Person则不能反序列化。

这是输出结果,可以看到我们反序列化一个Student对象,其中四个属性均有值,但是反序列化出来的结果则只有年级和学校,说明Person类没有成功序列化出来(因为没有使用Serializable接口),并且反序列化的时候调用了Person类的无参数构造函数

这是他的一个特性

父类如果没有实现序列化接口,那么将需要提供无参构造函数来重新创建对象

目的:重新初始化父类的属性,例如 Person 因为没有实现序列化接口,因此对应的 age和name 属性就不会被序列化,所以需要调用无参构造器将属性设置为null

Serializable特点3: 静态static成员变量是不能被序列化

static是在类加载的时候就定义的,是类的变量而不是对象的属性,所以不会被序列化

例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package Serialize;

import java.io.*;

class Person implements Serializable {
private String name;
public int age;
public static String sex = "man";

public Person(String name, int age) {
this.name = name;
this.age = age;

}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

public class Ser {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Person pysnow = new Person("pysnow", 20);
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bytes);
System.out.println("序列化");
out.writeObject(pysnow);

System.out.println("序列化数据:" + bytes.toString());

ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray()));
System.out.println("反序列化");
Person pysnoww = (Person) in.readObject();


System.out.println(pysnoww);
}
}

可以看到序列化后的数据里面没有sex这个字段

Serializable特点4:transient 标识的对象成员变量不参与序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package Serialize;

import java.io.*;

class Person implements Serializable {
private String name;
public int age;
public transient String sex = "man";

public Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}

public class Ser {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Person pysnow = new Person("pysnow", 20,"man");
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bytes);
System.out.println("序列化");
out.writeObject(pysnow);

System.out.println("序列化数据:" + bytes.toString());

ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray()));
System.out.println("反序列化");
Person pysnoww = (Person) in.readObject();


System.out.println(pysnoww);
}
}

简单来讲就是被transient标记的成员属性不参与序列化,但是还是作为一个正常的成员属性处理,跟static不同的是他是对象里面的而不是在类里面的变量,只不过transient标记的变量不能够存储下来

序列化ID

1
private static final long serialVersionUID = 1L;

序列化ID作用

我们知道如果我们要反序列化一个对象,那么客户端上就必须有一个对应的类。

比如说这里

1
2
3
4
5
6
class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
public int age;
public transient String sex = "man";
}

我们反序列化一个Person对象,但是我们对这个序列化的字节流做点修改,把int age改为String age,这样的情况下还能反序列化成功吗,很明显是不行的,因为这都不是一个类了,而JVM用来区分这个序列化字节流所表示的对象是不是对应客户端的某个类就用到了SerialVersionUID。

你可以理解为这是类class的一个private static final long属性,他默认是通过JVM根据class文件计算得到的,而由该class生成的序列化字节流的SerialVersionUID则跟该class文件计算的UID结果一样,而如果我们擅自修改了序列化字节流中某个成员属性的类型则会导致SerialVersionUID变化,在反序列化的时候就会不匹配SerialVersionUID

梳理一下

  • SerialVersionUID是用来区别类唯一性的UID标识
  • SerialVersionUID可以在类定义的时候通过private static final long serialVersionUID = 1L;进行修改
  • 如果为指定serialVersionUID属性,则有JVM自行计算
  • 不同JVM计算的SerialVersionUID不同,所以同一个序列化字节流在不同的JVM虚拟机中反序列化可能失败

影响序列化ID的因素

  • 手动去修改导致当前的 serialVersionUID 与序列化前的不一样。
  • 未设置 serialVersionUID 常量,则JVM 内部会根据类结构去计算serialVersionUID ,在类结构发生改变时serialVersionUID 发生变化。
  • 反序列和序列化操作的虚拟机不一样可能导致计算出来的 serialVersionUID 不一样
  • 类的方法 ,static变量和使用transient 修饰的实例变量 ,增加或删除实例变量 均不影响serialVersionUID

Externalizable接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public interface Externalizable extends java.io.Serializable {
/**
* The object implements the writeExternal method to save its contents
* by calling the methods of DataOutput for its primitive values or
* calling the writeObject method of ObjectOutput for objects, strings,
* and arrays.
*
* @serialData Overriding methods should use this tag to describe
* the data layout of this Externalizable object.
* List the sequence of element types and, if possible,
* relate the element to a public/protected field and/or
* method of this Externalizable class.
*
* @param out the stream to write the object to
* @exception IOException Includes any I/O exceptions that may occur
*/
void writeExternal(ObjectOutput out) throws IOException;

void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}

Externalizable接口继承了Serializable接口并定义了writeExternal和readExternal方法

也就是说Externalizable 将序列化和反序列化的工作完全交给了程序员,序列化和反序列化的逻辑全部由程序员编写,这样做的好处是程序员可以优化算法提升效率

Java 的序列化步骤与数据结构分析

writeObject

ObjectOutputStream构造函数

首先我们看一下这个类的构造函数,理解一下各个成员属性的含义方便后续代码的理解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public ObjectOutputStream(OutputStream out) throws IOException {
verifySubclass();
bout = new BlockDataOutputStream(out);
// 用于写入一些类元数据还有对象中基本数据类型的值
handles = new HandleTable(10, (float) 3.00);
subs = new ReplaceTable(10, (float) 3.00);
enableOverride = false;
// 支持重写序列化过程,为true则需要重写writeObjectOverride实现过程
writeStreamHeader();
// 写入头信息进序列化字节流中
bout.setBlockDataMode(true);
if (extendedDebugInfo) {
debugInfoStack = new DebugTraceInfoStack();
} else {
debugInfoStack = null;
}
}
1
2
3
4
protected void writeStreamHeader() throws IOException {
bout.writeShort(STREAM_MAGIC);
bout.writeShort(STREAM_VERSION);
}

第一个是 序列化协议 ,第二个是 序列化协议版本

writeObject

首先是序列化的数据,则这我将使用一个继承类方便观看writeObject对于父类的数据怎么处理的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package Serialize;

import java.io.*;

class Person {

public Person() {
System.out.println("父类无参数构造器");
}
public Person(String name, int age) {
this.name = name;
this.age = age;

}

private String name;
public int age;

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

class Student extends Person implements Serializable {
private int grade;
public String school_name;

public Student(String name, int age, int grade, String school_name) {
super(name, age);
this.grade = grade;
this.school_name = school_name;
}

@Override
public String toString() {
return "Student{" +
"grade=" + grade +
", school_name='" + school_name + '\'' +
'}';
}
}

public class Ser {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Student std = new Student("pysnow", 20,3,"SWPU");
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bytes);
System.out.println("序列化");
out.writeObject(std);

ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray()));
System.out.println("反序列化");
Student stdd = (Student) in.readObject();


System.out.println(stdd.toString());
}
}

ObjectOutputStream->writeObject

首先判断 enableOverride 是否为true,这里为false则进入 writeObject0 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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) {
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;
}

// 注意看后面这些,前面的if判断不会进入

// 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);
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) {
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);
} 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);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
} finally {
depth--;
bout.setBlockDataMode(oldMode);
}
}

1
desc = ObjectStreamClass.lookup(cl, true);

首先使用ObjectStreamClass.lookup方法传入当前对象的class对象作为参数

我们看下这个lookup方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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);
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()) {

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);
}
}

这个lookup可以获取序列化对象的属性信息以及其父类的属性值,至于父类的代码逻辑则在构造函数那

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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);

Class<?> superCl = cl.getSuperclass();
superDesc = (superCl != null) ? lookup(superCl, false) : null;
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);
// 通过反射获取类的serialVersionUID
try {
fields = getSerialFields(cl);
computeFieldOffsets();
} catch (InvalidClassException e) {
serializeEx = deserializeEx =
new ExceptionInfo(e.classname, e.getMessage());
fields = NO_FIELDS;
}

if (externalizable) {
cons = getExternalizableConstructor(cl);
} else {
cons = getSerializableConstructor(cl);
writeObjectMethod = getPrivateMethod(cl, "writeObject",
new Class<?>[] { ObjectOutputStream.class },
Void.TYPE);
readObjectMethod = getPrivateMethod(cl, "readObject",
new Class<?>[] { ObjectInputStream.class },
Void.TYPE);
readObjectNoDataMethod = getPrivateMethod(
cl, "readObjectNoData", null, Void.TYPE);
hasWriteObjectData = (writeObjectMethod != null);
}
writeReplaceMethod = getInheritableMethod(
cl, "writeReplace", null, Object.class);
readResolveMethod = getInheritableMethod(
cl, "readResolve", null, Object.class);
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;
}

通过lookup父类的class对象获取父类的属性信息

可以看到这个构造函数使用了大量的。

接着回到writeObject0函数

当对象继承了Serializable接口则调用writeOrdinaryObject,否则抛出NotSerializableException异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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);
writeClassDesc(desc, false);
handles.assign(unshared ? null : obj);
if (desc.isExternalizable() && !desc.isProxy()) {
writeExternalData((Externalizable) obj);
} else {
writeSerialData(obj, desc);
}
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}

很明显到这里就是序列化写数据的逻辑了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bout.writeByte(TC_OBJECT);
// 声明数据类型,写入\x115,如果obj是String的话那就是writeByte(TC_STRING)

writeClassDesc(desc, false);
// 写入类结构的描述信息,这个Desc的信息在前面的ObjectStreamClass类里面生成
// 这个writeClassDesc写入的逻辑是递归调用其对象最上层满足Serializable接口的父类
// 自上而下得依次写入数据

handles.assign(unshared ? null : obj);
if (desc.isExternalizable() && !desc.isProxy()) {
// 如果满足Externalizable接口则调用自定义的writeExternalData序列化方法
writeExternalData((Externalizable) obj);
} else {
// 否则
writeSerialData(obj, desc);
}

写入ClassDesc之前

1
[120, 112, 0, 17, 83, 101, 114, 105, 97, 108, 105, 122, 101, 46, 83, 116, 117, 100, 101, 110, 116, -110, 108, 77, -35, 41, 26, 23, -111, 2, 0, 2, 73, 0, 5, 103, 114, 97, 100, 101, 76, 0, 11, 115, 99, 104, 111, 111, 108, 95, 110, 97, 109, 101, 116, 0, 18, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 其他 924 个]

写入之后

接着最后进入writeSerialData,如果没有继承Externalizable接口的话

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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()) {
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);
bout.setBlockDataMode(false);
bout.writeByte(TC_ENDBLOCKDATA);
} finally {
curContext.setUsed();
curContext = oldContext;
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}

curPut = oldPut;
} else {
defaultWriteFields(obj, slotDesc);
}
}
}

这里可以看到desc.getClassDataLayout()获取的DataLayout就是将 ObjectStreamClass 封装了一下

接着遍历这个DataLayout数组,获取里面的desc,也就是获取序列化对象的描述

这里数组只有一个是因为Person这个父类没有继承Serializable接口

接着判断是否有writeObject这个自定义私有方法,如果有则调用我们自己定义的writeObject,否则则进入defaultWriteFields(obj, slotDesc)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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);

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());
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
}

这个defaultWriteFields则是写入对象数据的逻辑了

这里的大致过程可以理解为,先给当前序列化对象的基本数据类型赋值,比如说String,int等属性,接着对那些引用属性再进行writeObject0操作,对对象成员属性进行序列化,这是一个递归的过程

最后知道递归深度减少为0之后闭合序列化字节流,退出

readObject

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public final Object readObject()
throws IOException, ClassNotFoundException
{
if (enableOverride) {
return readObjectOverride();
}

// if nested read, passHandle contains handle of enclosing object
int outerHandle = passHandle;
try {
Object obj = readObject0(false);
handles.markDependency(outerHandle, passHandle);
ClassNotFoundException ex = handles.lookupException(passHandle);
if (ex != null) {
throw ex;
}
if (depth == 0) {
vlist.doCallbacks();
}
return obj;
} finally {
passHandle = outerHandle;
if (closed && depth == 0) {
clear();
}
}
}

同样的也是调用readObject0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
private Object readObject0(boolean unshared) throws IOException {
boolean oldMode = bin.getBlockDataMode();
if (oldMode) {
int remain = bin.currentBlockRemaining();
if (remain > 0) {
throw new OptionalDataException(remain);
} else if (defaultDataEnd) {
/*
* Fix for 4360508: stream is currently at the end of a field
* value block written via default serialization; since there
* is no terminating TC_ENDBLOCKDATA tag, simulate
* end-of-custom-data behavior explicitly.
*/
throw new OptionalDataException(true);
}
bin.setBlockDataMode(false);
}

byte tc;
while ((tc = bin.peekByte()) == TC_RESET) {
bin.readByte();
handleReset();
}

depth++;
try {
switch (tc) {
case TC_NULL:
return readNull();

case TC_REFERENCE:
return readHandle(unshared);

case TC_CLASS:
return readClass(unshared);

case TC_CLASSDESC:
case TC_PROXYCLASSDESC:
return readClassDesc(unshared);

case TC_STRING:
case TC_LONGSTRING:
return checkResolve(readString(unshared));

case TC_ARRAY:
return checkResolve(readArray(unshared));

case TC_ENUM:
return checkResolve(readEnum(unshared));

case TC_OBJECT:
return checkResolve(readOrdinaryObject(unshared));

case TC_EXCEPTION:
IOException ex = readFatalException();
throw new WriteAbortedException("writing aborted", ex);

case TC_BLOCKDATA:
case TC_BLOCKDATALONG:
if (oldMode) {
bin.setBlockDataMode(true);
bin.peek(); // force header read
throw new OptionalDataException(
bin.currentBlockRemaining());
} else {
throw new StreamCorruptedException(
"unexpected block data");
}

case TC_ENDBLOCKDATA:
if (oldMode) {
throw new OptionalDataException(true);
} else {
throw new StreamCorruptedException(
"unexpected end of block data");
}

default:
throw new StreamCorruptedException(
String.format("invalid type code: %02X", tc));
}
} finally {
depth--;
bin.setBlockDataMode(oldMode);
}
}

运行到判断对象所对应的类型为TC_OBJECT,则调用readOrdinaryObject读取对象信息

readOrdinaryObject

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
private Object readOrdinaryObject(boolean unshared)
throws IOException
{
if (bin.readByte() != TC_OBJECT) {
throw new InternalError();
}

ObjectStreamClass desc = readClassDesc(false);
desc.checkDeserialize();

Class<?> cl = desc.forClass();
if (cl == String.class || cl == Class.class
|| cl == ObjectStreamClass.class) {
throw new InvalidClassException("invalid class descriptor");
}

Object obj;
try {
obj = desc.isInstantiable() ? desc.newInstance() : null;
} catch (Exception ex) {
throw (IOException) new InvalidClassException(
desc.forClass().getName(),
"unable to create instance").initCause(ex);
}

passHandle = handles.assign(unshared ? unsharedMarker : obj);
ClassNotFoundException resolveEx = desc.getResolveException();
if (resolveEx != null) {
handles.markException(passHandle, resolveEx);
}

if (desc.isExternalizable()) {
readExternalData((Externalizable) obj, desc);
} else {
readSerialData(obj, desc);
}

handles.finish(passHandle);

if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())
{
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
if (rep != obj) {
handles.setObject(passHandle, obj = rep);
}
}

return obj;
}

首先实例化了一个指定的反序列化对象Student,目前还没有设置值

接着还是一样的判断是否继承Externalizable接口,如果是则执行用户自己定义的对象反序列化逻辑,否则则使用默认的反序列化逻辑,即字节流将中对象数据部分读取成对象

readSerialData

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
private void readSerialData(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 (slots[i].hasData) {
if (obj == null || handles.lookupException(passHandle) != null) {
defaultReadFields(null, slotDesc); // skip field values
} else if (slotDesc.hasReadObjectMethod()) {
SerialCallbackContext oldContext = curContext;
if (oldContext != null)
oldContext.check();
try {
curContext = new SerialCallbackContext(obj, slotDesc);

bin.setBlockDataMode(true);
slotDesc.invokeReadObject(obj, this);
} catch (ClassNotFoundException ex) {
handles.markException(passHandle, ex);
} finally {
curContext.setUsed();
if (oldContext!= null)
oldContext.check();
curContext = oldContext;
}

defaultDataEnd = false;
} else {
defaultReadFields(obj, slotDesc);
}

if (slotDesc.hasWriteObjectData()) {
skipCustomData();
} else {
bin.setBlockDataMode(false);
}
} else {
if (obj != null &&
slotDesc.hasReadObjectNoDataMethod() &&
handles.lookupException(passHandle) == null)
{
slotDesc.invokeReadObjectNoData(obj);
}
}
}
}

前面逻辑还是跟writeSerialData一样,获取desc的Data数组,遍历

直到执行到defaultReadFields这里,读取字节流中的对象属性值

后面的代码代码逻辑就跟WriteObject很像了

学完这些就有利于我们后面分析java序列化字节流的结构