构造块在对象序列化过程中完全不执行。java反序列化绕过构造器和构造块,直接还原字段值;字段显式初始化也不生效;需通过readobject手动补全初始化逻辑。

构造块在对象序列化过程中完全不执行。
序列化跳过构造块和构造器
Java 序列化(通过 ObjectOutputStream)仅保存对象的字段值(非 transient、非 static),反序列化(ObjectInputStream)时不调用任何构造器,也不执行任何构造代码块。JVM 直接分配内存并按字节流还原字段状态,绕过整个常规初始化流程。
- 反序列化创建对象是“无构造器路径”的特殊机制,连父类构造器都不会触发
- 构造代码块写在类中,但编译后被编译器插入到每个构造器开头;而反序列化根本不进构造器,自然跳过所有构造块
- 即使构造块里有日志、计数或资源初始化逻辑,反序列化时这些代码一句都不会运行
字段初始化表达式是否生效?取决于声明方式
构造块不执行,但字段的显式初始化表达式(如 private List<string> list = new ArrayList();</string>)在反序列化时也不会执行——因为该赋值属于构造路径的一部分,同样被跳过。
- 反序列化对象的字段值全部来自字节流,原始默认值(
null、0、false)或流中记录的值会被直接写入内存 - 唯一例外:
static字段不受影响(本就不参与序列化),transient字段恢复为默认值,且其声明时的初始化表达式也不会执行
如何确保反序列化后对象状态正确?
若对象依赖构造块或字段初始化来建立内部一致性(如集合非空、标志位设为 true),必须显式干预:
- 实现
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException方法,在其中手动补全初始化逻辑 - 让类实现
java.io.Serializable并提供serialVersionUID,避免因字段变更导致反序列化失败 - 对关键字段使用
transient并在readObject中重建(例如:重置集合、重新计算派生字段) - 避免在构造块中做不可逆操作(如注册监听器、启动线程),否则反序列化对象将缺失这些行为且无法自动恢复
验证方式很简单
在构造块和 readObject 中分别加入 System.out.println,序列化再反序列化一个实例,观察输出即可确认:只有 readObject 的日志出现,构造块日志完全沉默。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











