父类未实现Serializable时,子类单独实现该接口会导致父类字段不被序列化,反序列化时父类字段重置为默认值;应优先让父类实现Serializable,否则需在子类中手动处理父类字段或改用组合模式。

父类没实现 Serializable,子类单独实现接口时,父类字段不会被序列化——这不是 bug,而是 Java 序列化机制的明确规则。反序列化时,JVM 会调用父类的无参构造器重建父类部分,所有父类字段都变成默认值(null、0、false),而不是“保留原值”或“自动跳过”。关键在于:你得主动决定要不要补救,以及怎么补救。
让父类实现 Serializable(首选方案)
如果父类代码可控(比如你自己写的基类),这是最简洁、安全、可维护的做法:
- 给父类加上
implements Serializable - 显式声明
private static final long serialVersionUID = 1L; - 子类无需任何改动,原有字段和继承字段全部自动参与序列化
- 泛型类(如
Entity<ID>)也完全兼容,不用额外处理
子类手动接管父类字段(父类不可改时)
当父类来自 JDK、Spring、第三方 SDK 或其他无法修改的库,可在子类中重写 writeObject 和 readObject 方法:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
writeObject中先调用out.defaultWriteObject()写入子类字段,再用out.writeInt()、out.writeUTF()等逐个写出父类字段值 -
readObject中先调用in.defaultReadObject()恢复子类字段,再用in.readInt()、in.readUTF()读回并赋值给父类字段(需通过反射或 public setter) - 前提是父类必须有可访问的无参构造器(
public/protected/包级),否则反序列化直接失败 - 建议配合
serialPersistentFields显式声明字段顺序,避免流格式歧义
用组合替代继承(规避设计风险)
如果父类本就不该被序列化(比如它封装了资源、线程或外部状态),强行序列化反而带来隐患。此时更合理的做法是重构为组合关系:
立即学习“Java免费学习笔记(深入)”;
- 把父类实例作为子类的
transient字段 - 在
writeObject中只保存其关键标识(如 ID、名称等可序列化数据) - 在
readObject中根据标识重新构建或查找父类对象 - 避免序列化不可控状态,也绕开了构造器限制和字段丢失问题
必须同步注意的细节
无论选哪种方案,以下几点都不能忽略:
- 子类必须显式定义自己的
serialVersionUID(如= 2L),不能省略,也不能和父类共用 - 若父类不可序列化,它的字段在反序列化后一定是默认值,别依赖构造器赋初始值——那些逻辑不会被触发
- 内部类、匿名类容易隐含持有了不可序列化的外部类引用,优先改用静态内部类
- JSON(Jackson/Gson)或 Protobuf 等替代方案不走 Java 原生序列化流程,完全不受此限制

















