You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java反序列化异常:className字段未正常反序列化的问题求助

OpenJDK 17反序列化final字段className未恢复序列化值的原因解析

问题现象

反序列化SerialTest实例时,millisThen、dateTimeThen两个final字段能正常从序列化流中恢复原值,但className字段始终使用当前代码中定义的初始值,而非序列化时的取值:

  • 第一次运行(className = SerialTest.class.getName())序列化实例后,输出中className显示为SerialTest;
  • 修改代码将className改为"Why this?!"后再次运行(仅反序列化),输出中className显示为Why this?!,而非序列化时的SerialTest。

核心原因:Java序列化对编译期常量final字段的特殊处理

Java序列化机制会区分编译期常量表达式初始化的final字段和运行时初始化的final字段:

  • 编译期常量final字段:如果final字段是通过编译期常量表达式(如直接字符串字面量、基本类型常量等)初始化的,Java会将其视为常量,序列化时不会把该字段的值写入序列化流;反序列化时,直接使用当前类中定义的常量值,完全忽略流中可能存在的旧值。
  • 运行时初始化的final字段:如果final字段是通过运行时计算(如调用方法、动态生成值等)初始化的,序列化时会将字段值写入流中,反序列化时会从流中恢复原值,覆盖类中的初始定义。

对应到你的代码:

  • millisThen = System.currentTimeMillis()、dateTimeThen = LocalTime.now().toString():都是运行时调用方法生成的值,属于运行时初始化的final字段,因此会被正常序列化/反序列化。
  • 当你把className改为"Why this?!"时,这是一个编译期字符串常量,序列化机制会跳过该字段的反序列化逻辑,直接使用当前类中的常量值;而第一次序列化时,className = SerialTest.class.getName()是运行时调用方法生成的值,虽然会被写入流,但反序列化时因为当前类的className是编译期常量,所以流中的旧值被忽略。

验证逻辑

如果将第二次的className改为运行时初始化的形式,比如:

public final String className = new String("Why this?!");

此时className不再是编译期常量,反序列化时就会从流中读取第一次序列化时的SerialTest值,而不是当前的Why this?!。

解决方案

如果需要让className字段在反序列化时恢复序列化时的原值,需确保该字段不是通过编译期常量表达式初始化:

  • 改用运行时初始化方式(比如调用方法、new对象等);
  • 若必须使用常量形式,可考虑将字段改为非final,或通过readObject方法手动处理反序列化逻辑。

内容的提问来源于stack exchange,提问作者Dave The Dane

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 04:02:51