当前位置: 首页 > news >正文

JDK 25 重大兼容性 Bug

Java 25 是今年 9 月 16 号发布的,距今不足 2 月。已经发现了好几个 bug 了。这些 bug 当中,有几个已经修复了。于是,官方陆续的发布了几个对应 JDK 的修复版本,其中针对 Java 25,发布的是 JDK 25.0.1。在这个版本中,有一个影响 JDK 25 重大兼容性陷阱,那就是 java.time 序列化问题。本文将深度解析一下。

前言

JDK 25 带来了很多激动人心的新特性,我也写了好几篇陆续关于 JDK 25 新特性的文章。但在升级过程中,一些开发者们发现在使用过程中可能会遇到一个隐藏的"地雷",即 java.time 包中几个核心类的序列化兼容性问题。这个问题可能导致你的应用在升级后无法正常反序列化数据,甚至抛出 InvalidClassException。这个由 JDK-8367031 记录的 bug 终于被 PR 了。

受影响的核心类

收到影响的几个类,如下所示。

  • java.time.LocalDate
  • java.time.YearMonth
  • java.time.MonthDay
  • java.time.chrono.HijrahDate

问题的本质

JDK 25 与之前版本之间存在序列化不兼容性,但这里有一个重要的细节:只有 Class 对象的序列化受影响,实例对象的序列化是正常的

也就是说,升级 JDK 25 之前是好的,升级之后可能就会出现InvalidClassException问题。

技术根因分析

根据官方修复的https://bugs.openjdk.org/browse/JDK-8367031介绍显示,这个兼容性问题的根源可以追溯到 JDK-8334742,该变更是为了支持 Valhalla 项目中的值类(Value Classes)特性。Valhalla 项目旨在为 Java 引入值对象,结合面向对象编程的抽象性和简单原语的性能特征。

字段类型变更

官方为了实现更紧凑的内存布局,JDK 团队将以下字段的类型从 short 改为 byte

// 变更前的字段类型(JDK 24及之前)
privatefinalshort year;
privatefinalshort month;
privatefinalshort day;// 变更后的字段类型(JDK 25)
privatefinalint year;    // year保持int类型
privatefinalbyte month;  // month从short改为byte
privatefinalbyte day;    // day从short改为byte

JDK17

这个紧凑的内存布局,短了一字节,也短了命,让 JDK 25 的“字节”级事故坑到了不少老外尝鲜的网友。

为什么实例序列化不受影响?

不得不说,这里面有一个精妙的设计。即 java.time 类使用了序列化代理模式(Serialization Proxy Pattern)

// LocalDate 的序列化代理实现
private Object writeReplace() {return new Ser(Ser.LOCAL_DATE_TYPE, this);
}private Object readResolve() throws ObjectStreamException {throw new InvalidObjectException("A LocalDate proxy is required");
}

实际的序列化通过 java.time.Ser 类完成,该类实现了 Externalizable 接口,显式地编码和解码日期值,因此不受字段类型变更的影响。

问题示例

我们先看一个会出问题的代码(序列化 Class 对象)。

// 在JDK 24中序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("localdate-class.ser"));
// 下面这个在JDK 25中会出问题
oos.writeObject(LocalDate.class);  
oos.close();// 在JDK 25中反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("localdate-class.ser"));
// 下面的这行代码会抛出 InvalidClassException 异常
Class<?> clazz = (Class<?>) ois.readObject();  
ois.close();

下面我们来看一个序列化实例对象正常的代码。

// 在JDK 24中序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("localdate-instance.ser"));
// 这个在任何版本都正常
oos.writeObject(LocalDate.now());  
oos.close();// 在JDK 25中反序列化  
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("localdate-instance.ser"));
// 完美兼容
LocalDate date = (LocalDate) ois.readObject(); 
ois.close();

异常详情

当问题发生时,日志中可能会看到如下异常:

java.io.InvalidClassException: java.time.LocalDate; incompatible types for field dayat java.base/java.io.ObjectStreamClass.matchFields(ObjectStreamClass.java:2077)at java.base/java.io.ObjectStreamClass.checkClass(ObjectStreamClass.java:2049)at java.base/java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:908)at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2335)at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:2208)at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2218)at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1768)at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:547)

下面我们说一下官方的修复过程。

解决方案和修复过程

官方解决方案

经过社区反馈,OpenJDK 团队意识到了这个兼容性问题的严重性,决定回退 JDK-8334742 的变更,恢复受影响类的原始字段类型。

你看,官方评估过后,考虑到严谨性,首选的方案竟然是回退版本。要是国内企业,更可能是求快,赶鸭子上架,草草的改一下继续上线

修复的关键点

毕竟,退回还是最快最可靠最简单的修复方案了。后面我感觉还会继续改为 byte 类型,但具体时间未定,也可能在 JDK 26、27 中再现。

  1. 完全回退字段类型变更:将 byte 恢复为 short
  2. 保持 Valhalla 项目的兼容性:寻找其他方式支持值类特性
  3. 加强序列化兼容性测试:增加跨版本的序列化测试用例

临时解决方案

在官方注意到这个 bug 后,也有临时的解决方案。即,在修复发布之前,如果大家的应用受到此问题影响,可以采取下面 3 中临时方案。

  1. 避免序列化 Class 对象:改为序列化实例对象
  2. 使用自定义序列化:实现自己的序列化机制
  3. 保持版本一致:确保序列化和反序列化使用相同的 JDK 版本

受影响的应用场景

根据老外网友的讨论,了解到目前有一下一些框架受到影响。

  • 分布式缓存系统(如 Hazelcast、Apache Ignite)
  • RPC 框架中的类信息传输
  • 序列化框架(如 Kryo、FST)的兼容性层
  • 某些特定的反射工具库

最佳实践建议

尽量最小化颗粒度的序列化,以及避免序列化 Class 对象。

// 推荐:序列化具体的数据对象
public class DateEvent implements Serializable {private LocalDate date;  // 序列化实例private String description;
}// 避免:序列化Class对象
public class ClassHolder implements Serializable {private Class<?> dateClass;  // 可能引发兼容性问题
}

总结

实际上,JDK 25.0.1 共修复了 5 个 bug。如下图所示。

JDK-8367031 只是其中最显眼的一个罢了。它告诉我们,一个看似简单的字段类型变更,也可能引发意想不到的兼容性问题。

最后,切记,最先吃螃蟹是有风险的,即使在进行 JDK 升级时,也尽量做到充分的测试和评估是避免生产环境问题的关键。

另外JDK 25 作为最新的长期支持(LTS)版本,虽然带来了许多令人兴奋的新特性,但也引入了一些需要特别注意的重大兼容性变化。了解这些变化对于平稳升级至关重要。

下面这个表格汇总了目前已知的主要兼容性问题点,帮助您快速把握全局。

问题领域

具体表现

影响严重度

关键信息

架构支持

移除了对 32 位 x86 架构的支持

高(针对特定环境)

无法在 32 位 Linux x86 系统上运行 JDK 25

线程本地数据共享

引入 Scoped Values作为 ThreadLocal的现代替代方案

中(长期影响)

不立即破坏兼容性,但预示 ThreadLocal的未来使用方式变化

模块系统

新增 import module语法,可能引发命名冲突

低至中

若同时导入不同模块中的同名类,需显式指定以消除歧义

构造函数语法

允许在 super()this()调用前执行代码

低(行为变化)

改变了传统的构造函数执行模型,可能影响依赖此顺序的复杂继承结构

http://www.dtcms.com/a/601428.html

相关文章:

  • MyBatis多表联查返回List仅一条数据?主键冲突BUG排查与解决
  • c 做网站方便吗手机企业wap网站
  • el-table有固定列时样式bug
  • Vue项目中 安装及使用Sass(scss)
  • 珠海本地网站设计公司什么网站可以发布信息
  • UEFI+GPT平台一键安装Windows方法
  • GPT‑5 全面解析与开发者接入指南
  • 站优云seo优化页面模板这样选
  • dism++功能实操备份与还原
  • 动态型网站建设哪里便宜app开发需要用到哪些工具
  • 网站建设的什么是网站建设的第一阶段佛山市房产信息网
  • React 18
  • CVPR 2025|电子科大提出渐进聚焦Transformer:显著降低超分辨率计算开销
  • CTFHub Web进阶-Linux:动态装载
  • Nginx域名与SSL证书配置完整流程
  • 美食网站要怎么做自己做相册的网站
  • 全国 网站备案 数量电子设计工程官网
  • 一、UDP以太网帧格式
  • 网络协议设计原则简介和资料推荐
  • 有哪些程序网站品牌和商标的区别
  • C语言编译器选择指南 | 初学者实用教程
  • 小智机器人MCP
  • 对于给不良网站发律师函如何做收银系统哪个软件好
  • 网站管理工作总结安阳县事业单位招聘2021
  • RK-Android11-修改系统的鼠标光标
  • vs2022 IDE扩展无法卸载/VSI 插件卸载及实例清理
  • 华为OD机试 双机位A卷 - 智能驾驶 (JAVA Python C++ JS GO)
  • 广州网站制作实力乐云seo怎么提高网站的流量
  • 开源工具 yt-dlp 超简易上手版
  • 郑州网站优化外包顾问上海网站建设 分类广告