一个对象怎么变成字节流、又怎么变回来?这篇直接看字节,再用对照实验验证 transient、static、构造器、共享引用和 serialVersionUID 的行为(反序列化的类型过滤与安全见 ObjectInputFilter 一篇)。
P{name="ann", age=30} 序列化后 71 字节:
ac ed 00 05 73 72 00 06 4d 61 69 6e 24 50 2b 00
42 11 0f 92 1f fc 02 00 02 49 00 03 61 67 65 4c
00 04 6e 61 6d 65 74 00 12 4c 6a 61 76 61 2f 6c
61 6e 67 2f 53 74 72 69 6e 67 3b 78 70 00 00 00
1e 74 00 03 61 6e 6e
可读部分:····sr··Main$P+·B········I··ageL··namet··Ljava/lang/String;xp····t··ann
魔数 = aced(STREAM_MAGIC),版本 = 5,第 5 字节 0x73(0x73 = TC_OBJECT),第 6 字节 0x72(0x72 = TC_CLASSDESC)
类描述里的 serialVersionUID(默认由类结构算出)= 3098549184674013180
1000 个 P 的 ArrayList 序列化后 15110 字节,平均每个 P 15 字节(类描述只写一次,之后只写字段值和一个类引用)
对比:name + age 的原始内容只有 7 字节 创建对象:
Base() 构造器被调用
Child(String) 构造器被调用
反序列化(此时把 static shared 改成了 7):
Base() 构造器被调用
name=ann(普通字段被还原)
secret=null,count=0(transient 字段变成默认值;原来是 pw-123 与 9,字段初始化器 "pw"、5 也没有再运行)
static shared=7(static 不在流里,保持当前值)
同一个对象被两个字段引用:反序列化后 x == y 是 true(引用关系被保留),大小 79 字节
两个内容相同但不是同一个的对象:反序列化后 x == y 是 false,大小 82 字节[没有显式 UID] v1 写出 71 字节;v1 的 serialVersionUID = -3505072937817754330 v2 的 serialVersionUID = -8231464824746353903 读取失败:InvalidClassException: Person; local class incompatible: stream classdesc serialVersionUID = -3505072937817754330, local class serialVersionUID = -8231464824746353903 [显式 serialVersionUID = 1L] v1 写出 71 字节;v1 的 serialVersionUID = 1 v2 的 serialVersionUID = 1 读取成功:name=ann,email=null