透过序列化字节码看Java序列化

序列化数据的存储结构:

Java序列化后存储的信息包括:类元数据描述、类的属性、父类信息以及属性域的值。

编写一个测试类:

通过Junit进行序列化,生成序列化后的对象

使用strings打开ObjectSaver.obj 文件,

可以看到存储下来的可打印的字符信息如下:

二进制内容为:

第一部分

acedSTREAM_MAGIC 流的幻数,

用于标识序列化协议;

0005STREAM_VERSION 标识序列化协议的版本号;

一些标识性字符可以参考类:

ObjectStreamConstants ObjectOutputStream.writeStreamHeader()方法:

第二部分

74 标识TC_STRING

00 09 标识第一个String的长度为9个字节

e4 b8a5 e698 8ee6 988e 表示String类型的值:严明明

7372 标识TC_OBJECT 和 TC_CLASSDESC

000e 标识类名称长度为14个字节

6a61 7661 2e75 7469 6c2e 4461 7465 表示类型值:java.util.Date

686a 8101 4b59 7419 标识uid对象序列化ID的类型为long型,占用8个字节

03 这一个字节可能有十种值标识:参考java.io.ObjectStreamConstants#SC_*

0000 标识类属性个数,因为是Date类型,所以没有自定义属性

78 标识域类型TC_ENDBLOCKDATA ,因为属性个数为0,所以对象数据结束

70 再没有父类的标识

77 对象数据块开始,TC_BLOCKDATA

08 标识数据长度为8个字节

00 0001 5356 b659 b7 标识new Date()的对象时间戳long型,占用8个字节

78 TC_ENDBLOCKDATA 对象数据库结束标识

7372 0037 表示TC_OBJECT 和 TC_CLASSDESC ,且类名称长度为55个字节

636f 6d2e 6a61 7661 2e64 656d 6f2e 7365 7269 616c 697a 6162 6c65 2e53 6572 6961 6c69 7a61 626c 6554 6573 7424 4f62 6a65 6374 5361 7665 72 表示类名值:com.java.demo.serializable.SerializableTest$ObjectSaver

eb bbba f5cb 5ec7 4b 标识ObjectSaver对象序列化ID的类型为long型,占用8个字节 ,值为-1460368089309853877L,因为uid为静态属性,所以属于类元信息一部分

02 这一个字节可能有十种值标识,其中02标识SC_SERIALIZABLE,此类继承了Serializable接口

0003 标识类属性个数为 3 个,包含this;

49 域类型,49转十进制为73,73在ASC码中对应的是 I ,因为old为int类型

00 03 标识属性名称长度为3个字节

6f 6c64 标识属性名称为 old

4c 域类型,4c转十进制为76,76在ASC码中对应的是 L,因为name为String对象

00 04 标识属性名称长度为4个字节,name字符占四个字符

6e 616d 65 标识属性名称的值 name

74 标识TC_STRING一个新的字符串

0012 域类型长度为18个字节

4c6a 6176 612f 6c61 6e67 2f53 7472 696e 673b 对象类型签名 Ljava/lang/String; 包含封号

4c 域类型,4c转十进制为76,76在ASC码中对应的是 L

00 06 标识属性名称长度为6个字节

74 6869 7324 30 标识字符串 this$0

74 标识TC_STRING一个新的字符串

002d 域类型长度为45个字节

4c63 6f6d 2f6a 6176 612f 6465 6d6f 2f73 6572 6961 6c69 7a61 626c 652f 5365 7269 616c 697a 6162 6c65 5465 7374 3b Lcom/java/demo/serializable/SerializableTest;

78 标识域类型TC_ENDBLOCKDATA

上面为之类ObjectSaver的描述信息和元信息,下面为父类SerializableTest的描述信息和元信息

72 标识TC_CLASSDESC

002b 标识类名称长度为43个字节

63 6f6d 2e6a 6176 612e 6465 6d6f 2e73 6572 6961 6c69 7a61 626c 652e 5365 7269 616c 697a 6162 6c65 5465 7374 标识类字符串:com.java.demo.serializable.SerializableTest

1ae4 7592 b513 4860 标识ObjectSaver对象序列化ID的类型为long型,占用8个字节 ,值为-1937803012639770720L

02 这一个字节可能有十种值标识,其中02标识SC_SERIALIZABLE,此类继承了Serializable接口

00 01 标识类属性个数为1个

49 域类型,49转十进制为73,73在ASC码中对应的是 I ,因为old为int类型

0006 标识属性名称长度为6个字节

6661 7468 6572 标识father六个字符;

7870 TC_ENDBLOCKDATA 对象数据库结束标识,且没有父类

第三部分

接下来是对象属性域的值部分,按照从父类到子类的顺序写入域的值

0000 0001 标识的十进制为1,对应父类father的值为1

0000 001e 标识十进制为30,标识之类old的值为30

74 标识TC_STRING一个新的字符串

0009 标识占用9个字节

e6b5 8be8 af95 e7b1 bb 标识字符“测试类”

73 71 标识TC_OBJECT TC_REFERENCE

007e 0006 0000

总结 Java序列化算法的基本步骤

输出序列化的头部信息,包括序列化协议的幻数和版本;

基本类型按照一字节的类型标识、两字节类型长度、N个字节值

基本对象类型,7372标识OBJECT和CLASSDESC,两字节类型长度,8字节uid,一些辅助信息

复杂对象类型,第一步按照由子类到父类的顺序,递归的输出类的描述信息,知道不再有父类为止;类描述信息按照类元数据,类属性信息的顺序写入序列化流中;第二步按照由父类到之类的顺序,递归的输出对象域对象域的实际数据值;而对象的属性信息是按照基本类型到java对象类型的顺序写入序列化流中,其中java对象类型的属性会从第一步重新开始递归的输出,知道不再存在java对象类型的属性

-END-

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180619G1J09600?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券