可恢复异常指程序能通过重试、降级等策略继续执行,关键在问题的暂时性与可干预性,而非异常类型或是否checked;需结合上下文、原因链、状态码等人工判断并辅以自定义机制。

Java中没有内置的“可恢复异常”类型标记,Exception 和 RuntimeException 本身不携带“是否可恢复”的元信息——判断权完全在开发者手中,取决于业务语义和上下文。
什么是“可恢复异常”?
指发生后程序仍能通过重试、降级、补偿、切换策略等方式继续正常执行,而非必须中断流程或崩溃。例如网络超时、临时性资源不可用、第三方服务限流等。
- 不是由
throws声明与否决定,也不是由是否继承Exception决定 - 与是否是检查型异常(checked)无关:比如
SQLException是 checked,但连接池耗尽时重试可能恢复;而NullPointerException是 unchecked,但若源于外部 JSON 字段缺失且有默认值,也可视为可恢复 - 关键看异常背后的问题是否具有**暂时性**和**可干预性**
如何在代码中做可恢复性判断?
靠人工约定 + 辅助机制,常见做法:
- 自定义异常类,添加
isRecoverable()方法,并在构造时传入策略标识 - 用异常类名/包路径做白名单匹配,例如:
if (e.getClass().getName().contains("Timeout") || e instanceof SQLException) - 检查异常的
getCause()链,识别底层是否为SocketTimeoutException或ConnectException - 结合业务状态码(如 HTTP 503、429)或错误消息关键词(
e.getMessage().contains("rate limit"))辅助判断
避免踩坑的几个关键点
很多团队误把“能 catch”当成“可恢复”,结果掩盖了真正问题:
立即学习“Java免费学习笔记(深入)”;
- 不要仅凭异常类型做全局判断:同一个
IOException可能是磁盘满(不可恢复),也可能是网络抖动(可恢复) - 不要在日志里只打印
e.toString(),丢失getCause()和堆栈,导致无法区分根本原因 - 重试逻辑必须带退避(backoff)和最大次数,否则可能放大雪崩效应
- 对
InterruptedException的处理要特别小心——它本身是可恢复信号,但若忽略或吞掉,会破坏线程协作契约
可恢复性不是异常的固有属性,而是你对它“还能不能试试”的主观决策。最常被忽略的是:同一异常在不同调用点,恢复策略可能完全不同——比如支付接口超时要告警+人工介入,而商品查询超时直接走缓存降级。










