基础
序列化:将数据结构或对象转换成二进制串的过程。
反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。
调用writeObject()
序列化一个对象,是将其写入磁盘,以后在程序再次调用readObject()
时,根据wirteObject()
磁盘的文件重新恢复那个对象。Externalizable
接口扩展Serializable
,并增添两个方法:writeExternal()
和readExternal()
。在序列化和重新装配的过程中,会自动调用这两个方法。Externalizable
是Serializable
接口的子类,有时不希望序列化那么多,可以使用这个接口,这个接口的writeExternal()
和readExternal()
可以指定序列化哪些属性。
算法
算法一般会按步骤做如下事情:
- 将对象实例相关的类元数据输出。
- 递归地输出类的超类描述直到不再有超类。
- 类元数据完以后,开始从最顶层的超类开始输出对象实例的实际数据值。
- 从上至下递归输出实例的数据。
使用场景
语言里增加对象序列化的概念后,可提供对两种主要特性的支持。
- 远程方法调用(RMI)使本来存在于其他机器的对象可以表现出好象就在本地机器上的行为。将消息发给远程对象时,需要通过对象序列化来传输参数和返回值。
- 使用一个Java Bean时,它的状态信息通常在设计期间配置好。程序启动以后,这种状态信息必须保存下来,以便程序启动以后恢复;具体工作由对象序列化完成。
字节码解析
保存对象序列化字节码1
2
3
4
5
6
7
8
9package demo1;
import java.io.Serializable;
class Parent implements Serializable {
private static final long serialVersionUID = 1L;
int parentVersion = 10;
}
1 | package demo1; |
1 | package demo1; |
反序列化1
2
3
4
5
6
7
8
9public static void main(String[] args) throws IOException, ClassNotFoundException {
File file=new File("D:\\temp.out");
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);
SerialTest st1 = (SerialTest) ois.readObject();
System.out.println("getVersion():"+st1.getVersion());
ois.close();
fis.close();
}
控制台输出1
getVersion():66
temp.out输出1
2
3
4
5
6
7
8
9
10AC 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
字节码分析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
45AC ED:STREAM_MAGIC。声明使用序列化协议。
00 05:STREAM_VERSION。序列化协议版本。
0x73:TC_OBJECT。声明这是一个新的对象。
0x72:TC_CLASSDESC。 声明这里开始一个新Class。
00 0A:Class名字的长度。
53 65 72 69 61 6c 54 65 73 74:SerialTest,Class类名。
05 52 81 5A AC 66 02 F6:SerialVersionUID,序列化ID,如果没有指定,则会由算法随机生成一个8byte的ID。
0x02:标记号。该值声明该对象支持序列化。
00 02:该类所包含的域个数。
0x49:域类型。49 代表"I",也就是Int。
00 07:域名字的长度。
76 65 72 73 69 6F 6E:version,域名字描述。
0x4C:域的类型。
00 03:域名字长度。
63 6F 6E:域名字描述,con。
0x74:TC_STRING。代表一个new String,用String来引用对象。
00 09:该String长度。
4C 63 6F 6E 74 61 69 6E 3B:Lcontain;JVM的标准对象签名表示法。
0x78:TC_ENDBLOCKDATA,对象数据块结束的标志。
0x72:TC_CLASSDESC。声明这个是个新类。
00 06:类名长度。
70 61 72 65 6E 74:parent,类名描述。
0E DB D2 BD 85 EE 63 7A:SerialVersionUID,序列化ID。
0x02: 标记号. 该值声明该对象支持序列化。
00 01: 类中域的个数。
0x49:域类型. 49 代表"I",也就是Int。
00 0D:域名字长度。
70 61 72 65 6E 74 56 65 72 73 69 6F 6E:parentVersion,域名字描述。
0x78:TC_ENDBLOCKDATA,对象块结束的标志。
0x70:TC_NULL,说明没有其他超类的标志。
00 00 00 0A:10,parentVersion域的值。
00 00 00 42:66, version域的值。
0x73:TC_OBJECT,声明这是一个新的对象。
0x72:TC_CLASSDESC声明这里开始一个新Class。
00 07:类名的长度。
63 6F 6E 74 61 69 6E:contain,类名描述。
FC BB E6 0E FB CB 60 C7:SerialVersionUID,序列化ID。
0x02:Various flags. 标记号。该值声明该对象支持序列化。
00 01:类内的域个数。
0x49:域类型. 49 代表"I",也就是Int..
00 0E:域名字长度。
63 6F 6E 74 61 69 6E 56 65 72 73 69 6F 6E:containVersion,域名字描述。
0x78:TC_ENDBLOCKDATA对象块结束的标志。
0x70:TC_NULL,没有超类。
00 00 00 0B:11,containVersion的值。
字节分类意义
字节 | 说明 |
---|---|
AC-73 | 输出对象相关类的描述。 |
72-02 | SerialTest类的描述。 |
49-6E | int version=66。 |
4C-78 | contain con = new contain();这个有点特殊,是个对象。描述对象类型引用时需要使用JVM的标准对象签名表示法。 |
72-70 | 输出parent类的域描述,int parentVersion=100。 |
00-42 | 实例对象的实际值,Parent类,SerialTest类。 |
73-0B | 描述contain类的信息,要记住,现在还没有对contain类进行过描述。 |
源码解析
ObjectOutputStream
objectOutputStream#writeObject(obj)1
2
3
4
5
6
7
8
9
10
11
12
13
14public 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;
writeStreamHeader();
bout.setBlockDataMode(true);
if (extendedDebugInfo) {
debugInfoStack = new DebugTraceInfoStack();
} else {
debugInfoStack = null;
}
}
writeObject(obj)1
2
3
4
5
6
7
8
9
10
11
12
13
14public final void writeObject(Object obj) throws IOException {
if (enableOverride) {
writeObjectOverride(obj);
return;
}
try {
writeObject0(obj, false);
} catch (IOException ex) {
if (depth == 0) {
writeFatalException(ex);
}
throw ex;
}
}
writeObject0(Object obj, boolean unshared)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16if (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());
}
}
从上可以看出,如果对象没有实现Serializable
接口,在序列化的时候会抛出NotSerializableException
异常。
上述SerialTest是一个类对象,所以会执行writeOrdinaryObject(obj, desc, unshared)
。
writeOrdinaryObject(obj, desc, unshared)1
2
3
4
5if (desc.isExternalizable() && !desc.isProxy()) {
writeExternalData((Externalizable) obj);
} else {
writeSerialData(obj, desc);
}
如果对象实现Externalizable
接口,那么执行writeExternalData((Externalizable) obj)
。
如果对象实现的是Serializable
接口,那么执行的是writeSerialData(obj, desc)
。
writeSerialData()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
32ObjectStreamClass.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);
}
}
如果没有默认writer()
,执行defaultWriteFields()
defaultWriteFields()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
37private 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();
}
}
}
}
writeExternalData()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
26private void writeExternalData(Externalizable obj) throws IOException {
PutFieldImpl oldPut = curPut;
curPut = null;
if (extendedDebugInfo) {
debugInfoStack.push("writeExternal data");
}
SerialCallbackContext oldContext = curContext;
try {
curContext = null;
if (protocol == PROTOCOL_VERSION_1) {
obj.writeExternal(this);
} else {
bout.setBlockDataMode(true);
obj.writeExternal(this);
bout.setBlockDataMode(false);
bout.writeByte(TC_ENDBLOCKDATA);
}
} finally {
curContext = oldContext;
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
curPut = oldPut;
}
在此方法中 obj.writeExternal(this)
执行序列化对象实现的writeExternal
。
ObjectInputStream
objectInputStream#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
25public 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();
}
}
}
objectInputStream#readObject()
执行readObject0(false)
readObject0(false)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
60try {
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
,执行的方法是checkResolve(readOrdinaryObject(unshared));
readOrdinaryObject()
加载序列化对象的Class,如果可以实例化,那么创建它的实例desc.newInstance()
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
54private 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;
}