java反序列化绕过构造方法,由jvm直接分配内存并填充字段值,不执行静态块、实例块或构造器;成功依赖类结构、字节流与运行时环境严格匹配。

Java 对象反序列化通过 ObjectInputStream 恢复内存对象,本质是绕过构造方法、直接分配内存并填充字段值的过程。它不执行任何初始化逻辑(包括静态块、实例块、构造器),而是依赖 JVM 底层反射机制完成“非构造式实例化”。能否成功,关键不在代码写法,而在类结构、字节流和运行时环境三者是否严格匹配。
类必须可序列化且结构一致
被反序列化的类必须实现 java.io.Serializable 接口,并满足以下硬性要求:
- 声明
private static final long serialVersionUID,否则 JDK 会自动生成,类稍有改动(如增删字段)就极易导致InvalidClassException - 所有非
transient字段的名称、类型、声明顺序必须与序列化时完全一致;大小写、包名、类名差一个字符都会失败 - 若含引用类型字段(如
Address address),该引用类也必须可序列化,且其serialVersionUID匹配 - 父类若不可序列化,子类反序列化时父类字段将为默认值(
null/0/false),且可能抛出NotSerializableException
用 ObjectInputStream 正确读取并转换
流程看似简单,但每一步都有隐含约束:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- 必须用
FileInputStream(或SocketInputStream等)包装源数据,再套一层ObjectInputStream -
readObject()返回的是Object,需强制转为目标类型(如(User) ois.readObject()),建议配合instanceof防止ClassCastException - 必须捕获
IOException(流异常)和ClassNotFoundException(类在当前 classpath 中缺失) - 推荐使用 try-with-resources 自动关闭流,避免资源泄漏;手动 close 容易遗漏
字段恢复的规则你得心里有数
不是所有字段都能“原样回来”,JVM 有明确的跳过策略:
-
static字段:属于类,不属于对象图,反序列化时完全忽略,保持当前类加载后的值 -
transient字段:序列化时未写入字节流,反序列化时直接设为默认值(null、0、false) - 若需特殊处理(比如恢复
transient字段),可自定义private void readObject(ObjectInputStream in)方法,在其中手动赋值 - 枚举、Lambda、匿名内部类等不支持标准反序列化,多数情况会抛
InvalidClassException或ClassNotFoundException
安全风险不能视而不见
反序列化不是“读文件”那么简单,它是代码执行入口之一:
- 来自不可信源(如网络请求、用户上传文件)的字节流,可能触发恶意构造的链式调用,导致远程代码执行(RCE)
- 生产环境应禁用任意类反序列化,可用
ObjectInputStream.resolveClass()白名单校验类名 - 优先考虑 JSON/Protobuf 等更可控的数据交换格式,仅在可信封闭场景下使用 Java 原生序列化
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










