所有可序列化类必须显式声明serialVersionUID,父子类各自独立定义且互不影响;仅语义不兼容变更(如删字段、改类型)时才更新对应UID,否则反序列化将因版本不匹配直接失败。

显式声明、按变更性质决定是否更新、父子类各自独立维护——这是管理 serialVersionUID 的核心原则。不写它,JVM 自动生成的值会随编译环境或代码微调而变,上线后极易因 UID 不匹配直接抛 InvalidClassException,反序列化根本走不到字段赋值阶段。
所有可序列化类必须显式声明
在每个实现 Serializable 的类顶部,第一行就加上:
-
private static final long serialVersionUID = 1L;(数值可自定义,如20240901L,便于追踪) - 别依赖 IDE 自动生成的长哈希值(如
-7894253080042154647L),那只是对默认算法的模拟,仍不稳定 - 即使类里只有 getter/setter、没字段,只要实现了
Serializable,就得写 - Lombok 的
@Data会隐式引入桥接方法和字段,更需手动写死 UID,不能删掉 IDE 生成后的内容
只在不兼容变更时才更新 UID
UID 不是“每次改代码都要 +1”,而是标记协议层是否断裂。以下操作必须更新 UID:
- 删除或重命名非
transient字段 - 修改字段类型(如
int→Integer,String→byte[]) - 把非
transient字段改为static或transient - 调整继承关系(如父类突然实现
Serializable,或子类取消实现)
以下操作 UID 可保持不变,Java 序列化协议天然支持:
立即学习“Java免费学习笔记(深入)”;
- 新增非
transient字段(反序列化时自动设为null/0/false) - 给已有字段加
transient修饰 - 增加静态方法、重命名 getter/setter、加注解、调换方法顺序
- 缩小访问权限(
public→private)
父子类 UID 必须各自独立定义
子类不会继承父类的 UID,JVM 反序列化时会分别校验两者。常见错误是只在父类写 UID、子类留空或照抄同一数值。
- 父类建议从
1L开始,后续按自身演进递增(如2L、3L) - 子类哪怕没新增字段,也必须定义自己的 UID(如
101L、201L),且不能与父类相同 - 子类结构变更只影响自身 UID;父类改字段,只影响父类 UID,子类 UID 不用动
- 若父类未实现
Serializable,则它不参与序列化,子类只需管好自己的 UID,并确保父类有无参构造器
跨环境与历史数据的兜底处理
已上线服务若因 UID 变更导致旧数据无法读取,可临时兼容,但不宜长期使用:
- 用命令
serialver -classpath . com.example.MyClass查出旧类真实 UID - 将当前类的 UID 改为该值(如
= -7894253080042154647L) - 仅用于过渡,应尽快通过数据迁移(旧版反序列化 → 转 JSON → 新版重建对象)淘汰旧格式
- 不同 JDK 版本编译同一份源码可能导致默认 UID 不同,统一构建环境 + 显式声明是双重保险


















