序列化
问题背景:
- 代码修改:将实体类字段
long id改为Long id - 序列化机制:使用 JDK 序列化存储到 Redis
- 反序列化失败:重启服务后读取 Redis 数据时抛出
InvalidClassException
根本原因:
JDK 序列化依赖 类结构完整性,字段类型变更(基本类型→包装类型)导致类结构变化,触发序列化 ID 变化。
二、序列化 ID(serialVersionUID)的核心作用
| 场景 | 结果 |
|---|---|
| 未声明 serialVersionUID | JVM 根据类结构自动生成 ID ✅ 类结构不变:反序列化成功 ❌ 类结构变化(如 long→Long):自动生成的 ID 改变 → 反序列化失败 |
| 显式声明 serialVersionUID | 手动固定版本号 ✅ 类结构变化(如增删字段):可反序列化(需处理字段兼容) ❌ 字段类型变更:二进制结构不匹配 → 反序列化失败 |
📌 关键结论:
即使显式声明serialVersionUID,修改字段类型(如long→Long)仍会导致二进制数据不兼容!
三、序列化协议对比(JDK vs JSON)
| 特性 | JDK 序列化 | JSON 序列化 |
|---|---|---|
| 数据格式 | 二进制(不可读) | 文本(Key-Value 结构,可读) |
| 兼容性 | 强依赖类结构(类型严格匹配) | 弱依赖(仅需字段名匹配,类型可自动转换) |
| 跨语言 | 仅支持 Java | 多语言通用 |
| 空间效率 | 高(二进制压缩) | 低(文本冗余) |
| CPU 开销 | 高(压缩/解压复杂) | 低(文本解析简单) |
| 类型变更容忍度 | ❌ 不允许(如 long→Long 报错) | ✅ 允许(自动装箱/拆箱) |
四、解决方案
1. 兼容性修改原则
- 禁止直接修改字段类型:如必须修改,需通过数据迁移重新序列化历史数据。
- 显式声明
serialVersionUID:避免因方法/非关键字段变更导致意外失败。
2. 序列化协议选型
| 场景 | 推荐方案 |
|---|---|
| 纯 Java 服务、高压缩需求 | JDK 序列化(需严格兼容) |
| 多语言交互、字段易变性高 | JSON / Protocol Buffers |
容忍字段类型变更(如 long→Long) | 强制使用 JSON |
3. 补救措施
- 数据迁移:修改字段类型后,删除旧序列化数据或编写脚本转换历史数据。
- 双写策略:新字段用新名称(如
idLong),逐步迁移数据。
五、总结
- JDK 序列化是“脆弱”的:
long与Long二进制结构不同,类型变更必然导致反序列化失败。 - JSON 更适应迭代:
通过文本结构和自动类型转换,天然兼容字段类型升级。 - 安全迭代建议:
高兼容性系统:直接使用 JSON 序列化(如 Spring Data Redis 默认 JSON 序列化器)。// 显式声明 serialVersionUID 是底线,但非万能! public class User implements Serializable { private static final long serialVersionUID = 1L; // 固定版本号 private Long id; // 优先使用包装类型(兼容 null) }

被折叠的 条评论
为什么被折叠?



