Java 对象序列化

序列化(serialization)在计算机科学的数据处理中,是指将数据结构或对象状态转换成可取用格式(例如存成文件,存于缓冲,或经由网络中发送),以留待后续在相同或另一台计算机环境中,能恢复原先状态的过程。 - 维基百科

在 Java 中,序列化是指将 Java 对象转换为字节序列的过程,而反序列化是指将字节序列转换为 Java 对象的过程。

序列化的主要作用包括两个方面:

  1. 实现网络中对象的传输,即在网络进程间传递对象;
  2. 实现内存中对象的持久化,即将对象保存到本地磁盘中;

Serializable 接口

1
2
3
4
5
6
7
8
9
/**
* Serializability of a class is enabled by the class implementing the
* java.io.Serializable interface. Classes that do not implement this
* interface will not have any of their state serialized or
* deserialized. All subtypes of a serializable class are themselves
* serializable. The serialization interface has no methods or fields
* and serves only to identify the semantics of being serializable.
* /
public interface Serializable {}

一个对象要实现持久化,必须要实现 Serilizable 接口,而且对象的属性也要实现 Serilizable 接口。
使用简单但是开销大,序列化和反序列化都需要大量的 I/O 操作。

serialVersionUID: 保证版本号的一致性。

Externalizable 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* Only the identity of the class of an Externalizable instance is
* written in the serialization stream and it is the responsibility
* of the class to save and restore the contents of its instances.
*/
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.
*/
void writeExternal(ObjectOutput out) throws IOException;

/**
* The object implements the readExternal method to restore its
* contents by calling the methods of DataInput for primitive
* types and readObject for objects, strings and arrays. The
* readExternal method must read the values in the same sequence
* and with the same types as were written by writeExternal.
*/
void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}

实现该接口的类需要手动实现序列化和反序列化的操作。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private static class Person implements Externalizable {
private int id;
//此时用 transient 修饰没有意义。因为自己控制序列化过程。
private transient String name;

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

//实现序列化
@Override public void writeExternal(ObjectOutput objectOutput) throws IOException {
objectOutput.writeObject(name);
objectOutput.writeObject(id);
}

//实现反序列化
@Override public void readExternal(ObjectInput objectInput) throws IOException, ClassNotFoundException {
name = (String) objectInput.readObject();
id = (Integer) objectInput.readObject();
}
}

通过 ObjectInputStream/ObjectOutputStream 实现序列化:

1
2
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(filePath));
outputStream.writeObject(obj);

ObjectOutputStream.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
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
public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants{

public final void writeObject(Object obj) throws IOException {
writeObject0(obj, false);
}

private void writeObject0(Object obj, boolean unshared){
// remaining cases
// BEGIN Android-changed
if (obj instanceof Class) {
writeClass((Class) obj, unshared);
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
// END Android-changed
} else 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) {//序列化 Serializable 或 Externalizable 的对象
writeOrdinaryObject(obj, desc, unshared);
}
...

}

/**
* Writes representation of a "ordinary" (i.e., not a String, Class,
* ObjectStreamClass, array, or enum constant) serializable object to the
* stream.
*/
private void writeOrdinaryObject(Object obj, ObjectStreamClass desc, boolean unshared) {
if (desc.isExternalizable() && !desc.isProxy()) {
writeExternalData((Externalizable) obj);
} else {
writeSerialData(obj, desc);
}
}

private void writeExternalData(Externalizable obj) throws IOException {
obj.writeExternal(this);//回调 writeExternal 方法,this 是 ObjectOutputStream 对象。
}

private void writeSerialData(Object obj, ObjectStreamClass desc) {
for (int i = 0; i < slots.length; i++) {
ObjectStreamClass slotDesc = slots[i].desc;
if (slotDesc.hasWriteObjectMethod()) {
//反射调用
slotDesc.invokeWriteObject(obj, this);
} else {
defaultWriteFields(obj, slotDesc);
}
}
}

}

ObjectStreamClass.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ObjectStreamClass implements Serializable {
private Method writeObjectMethod;

/**
* Invokes the writeObject method of the represented serializable class.
* Throws UnsupportedOperationException if this class descriptor is not
* associated with a class, or if the class is externalizable,
* non-serializable or does not define writeObject.
*/
void invokeWriteObject(Object obj, ObjectOutputStream out) {
writeObjectMethod.invoke(obj, new Object[]{ out });
}
}

transient 关键字

  1. 一旦变量被 transient 修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。
  2. transient 关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被 transient 关键字修饰的。变量如果是用户自定义类变量,则该类需要实现 Serializable 接口。
  3. 被 transient 关键字修饰的变量不再能被序列化,一个静态变量不管是否被 transient 修饰,均不能被序列化。反序列化后类中 static 型变量的值为当前 JVM 中对应 static 变量的值,这个值是 JVM 中的,不是反序列化出来的。

序列化后的十六进制:

1
2
3
4
5
6
7
8
9
10
AC ED 00 05 73 72 00 0A 53 65 72 69 61 6C 54 65
73 74 05 52 81 5A AC 66 02 F6 02 00 02 49 00 07
76 65 72 73 69 6F 6E 4C 00 03 63 6F 6E 74 00 09
4C 63 6F 6E 74 61 69 6E 3B 78 72 00 06 70 61 72
65 6E 74 0E DB D2 BD 85 EE 63 7A 02 00 01 49 00
0D 70 61 72 65 6E 74 56 65 72 73 69 6F 6E 78 70
00 00 00 0A 00 00 00 42 73 72 00 07 63 6F 6E 74
61 69 6E FC BB E6 0E FB CB 60 C7 02 00 01 49 00
0E 63 6F 6E 74 61 69 6E 56 65 72 73 69 6F 6E 78
70 00 00 00 0B
  • AC ED 序列化协议标识
  • 00 05 流版本号
  • 73 表示这是一个新对象
  • 72 表示这是一个新的类

readResolve() 方法

For Serializable and Externalizable classes, the readResolve method allows a class to replace/resolve the object read from the stream before it is returned to the caller.

可以在单例模式中使用:

1
2
3
4
5
6
7
8
public class Singleton{
...
private Object readResolve() throws ObjectStreamException {
// instead of the object we're on,
// return the class variable INSTANCE
return INSTANCE;
}
}

java.lang.String.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static final Comparator<String> CASE_INSENSITIVE_ORDER
= new CaseInsensitiveComparator();
private static class CaseInsensitiveComparator
implements Comparator<String>, java.io.Serializable {
// use serialVersionUID from JDK 1.2.2 for interoperability
private static final long serialVersionUID = 8575799808933029326L;

public int compare(String s1, String s2) {
...
}

/** Replaces the de-serialized object. */
private Object readResolve() { return CASE_INSENSITIVE_ORDER; }
}

创建对象的几种方式

  1. new 关键字
  2. Class.newInstance()
  3. Constructor.newInstance()
  4. clone()
  5. 反序列化

(1)(2)(3) 会调用构造函数, (4)(5) 不会调用类的构造函数。

参考

[1] The Java serialization algorithm revealed
[2] The readResolve Method - Oracle