Java 8 与 JDK 17 在异常处理上无本质语法差异,兼容代码应坚持传统多 catch 分支、标准 try-with-resources 语法,替换已移除类(如 java.util.Base64 替代 sun.misc.BASE64Encoder),规避反射访问限制,并统一用字符串判断异常类型而非版本分支。

Java 8 和 JDK 17 在异常处理层面存在实质性差异,尤其体现在异常变量的语法支持、模块访问限制、以及部分异常类的移除或重构上。要编写真正兼容两者的代码,不能靠“写两次”或“条件编译”,而应聚焦于**语法共性、API 替代路径、运行时兜底逻辑**三个层面。
用标准 try-with-resources 语法,避开 JDK 17 新增的异常变量特性
JDK 17 支持在 catch 块中使用 模式匹配 for instanceof(如 catch (Exception e) when (e instanceof IOException)),但该语法在 JDK 8 完全不可用,且无法降级兼容。因此,兼容代码必须回避所有 JDK 9+ 引入的异常相关新语法。
- 坚持使用传统多 catch 分支:
catch (IOException e) { ... } catch (SQLException e) { ... } - 避免
catch (var e)或任何带when子句的写法 - 资源声明保持 JDK 7 起就支持的格式:
try (FileInputStream fis = new FileInputStream("a.txt")) { ... }—— 此语法 JDK 8 和 17 均完全支持
处理因模块化引发的异常访问失败(如 InaccessibleObjectException)
JDK 17 默认阻止反射访问核心类私有成员,一旦触发 InaccessibleObjectException,JDK 8 环境下不会抛此异常,但代码若依赖反射操作(如某些 ORM 或序列化工具),需统一规避或适配。
- 不主动调用
Field.setAccessible(true)访问java.base中被封装的字段(如String.value) - 改用公开 API 替代:例如用
String.getBytes(StandardCharsets.UTF_8)代替反射读取内部 char 数组 - 若必须保留反射逻辑,应在启动参数中统一加
--add-opens(仅影响运行时,不影响源码编译);但注意该参数对 JDK 8 无效且无害,可安全保留在脚本中
应对已删除/迁移的异常相关类(如 sun.misc.BASE64Encoder、javax.xml.bind)
这些不是“异常类型”本身变化,而是抛出异常的上下文类被移除,导致编译失败或 NoClassDefFoundError —— 这类问题必须在编译期拦截并替换。
- 将
sun.misc.BASE64Encoder全部替换为java.util.Base64.getEncoder()(JDK 8u60+ 和 JDK 17 均支持) - 将
javax.xml.bind.JAXBException及相关类,替换为 Jakarta EE 对应包:jakarta.xml.bind.JAXBException,并显式引入jakarta.xml.bind-api和org.glassfish.jaxb:jaxb-runtime - 检查是否使用了
javafx.util.Pair等非标准类——它们在 JDK 17 中被彻底移除,应改用AbstractMap.SimpleEntry或自定义轻量 Pair
统一异常日志与分类逻辑,避免版本判断分支
不要写 if (System.getProperty("java.version").startsWith("1.8")) { ... } else { ... } 来区分异常处理方式——这会增加维护成本且易出错。推荐做法是:
- 用通用异常基类捕获(如
Exception),再通过e.getClass().getName()做字符串判断(兼容所有 JDK) - 对关键异常做封装:例如定义
IoFailure包装IOException和UncheckedIOException,屏蔽底层差异 - 日志框架(如 SLF4J)本身已兼容多 JDK 版本,直接使用
logger.error("msg", e)即可,无需版本适配










