必须先调用 getcause() 获取真实异常并判空,再用 instanceof 分类型处理:nullpointerexception 触发告警,sqlexception 按 sqlstate 分策略,businessexception 封装返回,ioexception 进一步查根因;需在同作用域强转,必要时递归获取 root cause。
getcause() 解包后的 throwable,必须先判空再用 instanceof 做类型分支,才能安全路由到不同处理逻辑。它不是直接对 executionexception 判断,而是对底层真实异常做识别——比如 nullpointerexception 走兜底告警,sqlexception 走重试,自定义业务异常走降级。
先解包再判断:两步不能颠倒
Future.get() 抛出的 ExecutionException 只是容器,原始异常藏在 cause 里。跳过 getCause() 直接对 e 做 instanceof,永远匹配不到你写的业务异常。
- ✅ 正确顺序:catch (ExecutionException e) → Throwable cause = e.getCause(); → if (cause != null) { ... }
- ❌ 错误写法:if (e instanceof BusinessException) —— BusinessEx 不在 e 里,而在 cause 里
- ⚠️ 注意:cause 可能为 null(极少数线程池实现未设 cause),不判空会导致 NPE
按异常类型做精准路由
拿到 cause 后,用 instanceof 区分具体类型,每种类型对应明确的响应策略,避免“统一打印日志+抛 RuntimeException”这种粗放做法。
- cause instanceof NullPointerException → 记录 warn 级日志,触发监控告警(说明代码有空指针隐患)
- cause instanceof SQLException → 检查 SQLState,如 "08001" 表示连接失败,走快速失败;"23505" 表示唯一键冲突,转业务重试
- cause instanceof BusinessException → 提取 errorCode 和 message,封装成 API 返回体,前端可直接展示
- cause instanceof IOException → 进一步用 cause.getCause() 查是否是 NoSpaceLeftOnDeviceException,是则触发磁盘清理流程
避免强转陷阱:instanceof 后立即使用,不跨作用域
判断完类型后,强转操作要和 instanceof 写在同一作用域内,尤其在并发流或 CompletableFuture 中,防止多线程争抢变量或状态不一致。
- ✅ 安全写法:if (cause instanceof SQLException sqlEx) { handleDbError(sqlEx); }(Java 16+ 模式匹配)
- ✅ 兼容写法:if (cause instanceof SQLException) { SQLException sqlEx = (SQLException) cause; handleDbError(sqlEx); }
- ❌ 危险写法:SQLException sqlEx = null; if (cause instanceof SQLException) sqlEx = (SQLException) cause; // 后续在另一线程用 sqlEx
结合 root cause 做深度归因
某些框架(如 Feign + OkHttp)会把原始异常套上 3–5 层包装。只取 getCause() 一层不够,需递归提取 root cause 才能命中真正类型。
- 调用自定义工具方法:Throwable root = getRootCause(cause);
- 再对 root 做 instanceof 分支,例如 root instanceof TimeoutException → 判定为下游超时,而非本端逻辑错误
- 限制递归深度(如 16 层),防止循环引用导致栈溢出
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











