49 | 序列化版本号 serialVersionUID 不写会怎样?
摘要:
Serializable类不写serialVersionUID,JVM 会根据类结构自动生成一个。类结构稍有变化,自动生成的 UID 就变了,反序列化直接失败。
一、问题现象
// ❌ 没写 serialVersionUIDclassUserimplementsSerializable{privateLongid;privateStringname;}把对象序列化到文件,然后给User加一个字段:
classUserimplementsSerializable{privateLongid;privateStringname;privateIntegerage;// 新增字段}再反序列化:InvalidClassException。
二、踩坑现场
场景:微服务间用 JDK 序列化传输
// ❌ 没写 serialVersionUID,发版后反序列化失败classOrderDTOimplementsSerializable{privateLongorderId;privateLonguserId;}服务 A 发版(加了字段),服务 B 没发版,服务 B 反序列化失败。
三、原理解析
3.1 serialVersionUID 的作用
// 反序列化时会检查:// 字节流里的 serialVersionUID == 当前类的 serialVersionUID?// 相等 → 继续反序列化// 不相等 → InvalidClassException3.2 不写时 JVM 怎么生成?
JVM 根据类的结构(字段、方法、修饰符…)计算一个哈希值。
问题:只要类结构稍有变化(加字段、改方法),哈希值就变。
// 版本 1(没写 serialVersionUID)classUserimplementsSerializable{privateLongid;}// 版本 2(加字段,自动生成的 UID 变了)classUserimplementsSerializable{privateLongid;privateStringname;// 新增}// 反序列化版本 1 的字节流 → InvalidClassException四、正确写法
4.1 实现 Serializable 就写 serialVersionUID
// ✅ 正确:明确指定 serialVersionUIDclassUserimplementsSerializable{privatestaticfinallongserialVersionUID=1L;privateLongid;privateStringname;}版本兼容规则:
- 字段只增不减 →
serialVersionUID不变,反序列化兼容 - 字段删除或类型改变 →
serialVersionUID+1,不兼容
4.2 IDEA 自动生成
右键 → Generate → serialVersionUID
(需开启Serializable class without serialVersionUID警告)
4.3 不需要 JDK 序列化的场景
// ✅ JSON 序列化(Jackson)不依赖 serialVersionUID// 所以 DTO 如果只用 JSON 传输,不写也没关系classUser{privateLongid;privateStringname;}但:如果不确定未来会不会用 JDK 序列化(如放入
HttpSession、用ObjectOutputStream),还是写上。
五、最佳实践
✅ Serializable 使用的 3 条铁律
- 实现
Serializable必须写serialVersionUID - 字段只增不减时
serialVersionUID不变(兼容) - 如果确定只用 JSON 序列化,可以不写(但写上更保险)
🔍 如何快速发现漏写?
IDEA 开启Serializable class without serialVersionUID警告(默认开启)。
六、小结
- 不写
serialVersionUID,JVM 根据类结构自动生成 - 类结构稍有变化,自动生成的 UID 就变,反序列化失败
- 实现
Serializable必须写serialVersionUID - 字段只增不减 → UID 不变(兼容)
- JSON 序列化(Jackson)不依赖
serialVersionUID
第七辑完。—— 下一篇进入第八辑:综合实战篇,首篇:一次生产 OOM 排查实录。