java反序列化不调用任何构造方法,而是由jvm直接分配内存并按继承链和声明顺序还原非transient字段;实例变量赋值、初始化块、构造器逻辑均不执行,仅static初始化和readobject/readresolve方法可控。

Java 反序列化时不会调用任何构造方法(包括无参、有参、私有构造器),对象是通过 JVM 底层机制直接分配内存并填充字段值的。因此,成员变量的初始化顺序与常规 new 实例完全不同。
反序列化绕过构造器的本质
ObjectInputStream 在反序列化时,会:
- 跳过所有构造方法(哪怕你写了 private NoArgsConstructor(),它也不会执行)
- 直接在堆上分配对象内存(类似 Unsafe.allocateInstance)
- 按类继承链从父类到子类、按字段声明顺序(非初始化块顺序),逐个还原 transient 以外的非静态字段值
- 如果字段类型本身也实现了 Serializable,则递归反序列化其内容
成员变量初始化语句和初始化块是否执行?
不执行。包括:
- 实例变量的直接赋值(
private int x = 10;)→ 被忽略,取流中保存的值 - 实例初始化块(
{ y = 20; })→ 完全不运行 - 构造器中的逻辑(
this.z = compute();)→ 根本不触发
唯一例外:static 字段初始化仍按类加载时执行(与反序列化无关),而 readObject() 或 readResolve() 方法若存在,则会在字段填充后被回调 —— 这是手动补救的唯一可控入口。
继承场景下的字段还原顺序
假设:class A implements Serializable { int a = 1; },class B extends A { int b = 2; }
- 反序列化 B 实例时,JVM 先还原 A 中声明的字段(a),再还原 B 中声明的字段(b)
- 但 a 和 b 的值都来自字节流,不是来自
a = 1或b = 2的默认值 - 如果父类 A 没实现 Serializable,则 A 中字段会被默认初始化(如 int → 0),且其构造器仍不会执行 —— 此时 A 的字段无法从流中恢复,除非 A 提供
readObject()
如何验证和控制初始化行为
可在类中添加以下方法观察执行时机:
-
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException:字段填充后立即调用,适合做校验、计算派生字段、重置 transient 状态 -
private Object readResolve():整个反序列化完成后调用,常用于单例修复(返回已有实例)
注意:这两个方法必须是 private,且签名严格匹配,否则会被忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











