Java对象序列化是将对象转为字节流以实现持久化或网络传输,需实现Serializable接口;serialVersionUID用于版本校验,建议显式声明;static、transient及不可序列化类型的字段不参与序列化;父类未实现Serializable时其字段不被序列化且需无参构造器;反序列化不可信数据存在安全风险。

Java 对象序列化是面试高频考点,回答要抓住“为什么用、怎么用、注意什么”三层逻辑,重点突出对 serialVersionUID、transient、继承与静态字段行为、以及反序列化安全风险的理解。
什么是序列化?为什么需要它?
序列化是把 Java 对象转换成字节流的过程,用于持久化(如写入文件、数据库)或网络传输(如 RMI、RPC)。反序列化则是还原过程。关键点在于:只有实现了 java.io.Serializable 接口的类才能被默认序列化——这个接口是标记接口,不定义任何方法,仅起语义标识作用。
说明:JVM 通过该标记启用内置序列化机制;若未实现,调用 ObjectOutputStream.writeObject() 会直接抛 NotSerializableException。
serialVersionUID 的作用和最佳实践
serialVersionUID 是序列化运行时为每个可序列化类生成的版本号。当反序列化时,JVM 会校验字节流中的 UID 和当前类的 UID 是否一致,不一致则抛 InvalidClassException。
- 显式声明更可靠:编译器自动生成的 UID 依赖类结构(字段名、类型、访问修饰符、接口等),稍有改动就变化,极易导致线上兼容问题
- 建议始终手动定义为
private static final long serialVersionUID = 1L;(或用 IDE 生成的固定值) - 版本升级时,若字段语义不变(如仅新增
transient字段或 getter/setter),可保持 UID 不变;若删改非 transient 字段,则应更新 UID 或提供readObject()兼容逻辑
哪些字段不会被序列化?如何控制?
以下三类字段默认不参与序列化:
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
- static 字段:属于类而非实例,序列化针对对象状态,自然排除
-
transient 字段:显式标记为“暂态”,如密码、缓存、线程相关对象(
ThreadLocal、Socket)等敏感或不可序列化资源 -
未实现 Serializable 的成员对象:若某字段类型本身不可序列化,且未标记为
transient,序列化时直接报错
进阶控制:可通过重写 writeObject() 和 readObject() 方法自定义序列化逻辑(例如加密字段、按需加载懒初始化对象)。
继承、父类与反序列化安全风险
子类实现 Serializable 时,父类是否可序列化决定其字段是否被保存:
- 父类也实现了
Serializable:全部实例字段(包括 private)都会被序列化 - 父类未实现:父类字段不会被序列化;反序列化时,父类部分通过其无参构造器初始化(要求必须存在,否则抛
InvalidClassException)
安全提醒:反序列化不受控字节流可能触发恶意代码(如通过 readObject() 构造攻击链)。生产环境应避免直接反序列化不可信数据;可考虑使用白名单机制(如 ObjectInputStream.resolveClass() 拦截)、或切换到 JSON/Protobuf 等更安全的序列化方案。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










