受检异常的恢复逻辑应围绕“是否该由当前层兜底”决策:能真正恢复才捕获,否则交由上层;恢复动作须有明确边界,包括提供fallback、局部修复、资源清理、屏蔽细节;拒绝仅日志后原样抛出、盲目包装为非受检异常、空catch等伪处理;重新抛出须保留原始异常上下文;日志与抛出不可并行;需按异常类型匹配策略,如临时性异常重试、非关键步骤跳过、核心依赖失效降级、不可恢复异常终止。

受检异常的恢复逻辑不是“一捕了之”,而是围绕“是否该由当前层兜底”来决策。能真正恢复,才捕获;不能恢复,就明确交给上层——这是设计恢复逻辑的核心前提。
明确恢复动作的边界
只有当方法上下文具备执行具体恢复能力时,捕获才有意义:
- 提供 fallback:如读取配置失败时加载默认值,网络请求失败时返回缓存数据
- 局部修复:文件被占用时自动切换临时目录,连接超时后触发一次重试
- 资源清理:用 try-with-resources 确保流、连接及时释放,避免泄漏
- 屏蔽细节:UI 层将底层 FileNotFoundException 转为“系统繁忙,请稍后再试”这类用户可理解提示
拒绝无效的“伪处理”
以下做法看似完成编译要求,实则破坏异常语义和调用链责任:
- 仅记录日志再原样抛出:
catch (IOException e) { log.error(...); throw e; }—— 没新增任何业务逻辑,不如直接 throws - 盲目包装为非受检异常:
throw new RuntimeException(e)—— 绕过编译检查,调用方失去知情权与处理机会 - 空 catch 或只调用
e.printStackTrace()—— 异常信号丢失,问题无法定位
重新抛出必须保留上下文
若需包装后向上抛,必须通过带 cause 的构造器传递原始异常:
- 正确写法:
throw new ServiceException("数据库操作失败", originalException); - 错误写法:
throw new ServiceException("数据库操作失败");—— 堆栈断层,排查困难 - 日志与抛出不可并行:要么记录后静默兜底(已真正解决),要么抛出交由上层决定,二者混用会导致重复日志干扰分析
按异常类型匹配恢复策略
恢复逻辑需结合异常本质动态选择,而非统一模板:
- 临时性异常(如网络超时、短暂锁冲突)→ 重试机制(建议指数退避 + 最大尝试次数)
- 非关键步骤异常(如日志格式解析失败)→ 跳过并记录,保障主流程继续执行
- 核心依赖失效(如第三方服务不可用)→ 降级(切本地缓存、返回默认值、启用备用通道)
- 不可恢复异常(如配置致命错误、权限缺失)→ 明确终止,抛出带上下文的异常,阻止错误蔓延


















