避免过度捕获异常的关键是精准分层处理:只在必要层级捕获,中间层优先上抛;禁用空catch块;日志统一收口至最外层(如@ControllerAdvice);区分受检异常、运行时异常和Error,按类型采取修复、包装或不捕获策略。

避免过度捕获异常,关键在于让异常“该出现的地方出现、该记录的地方记录、该终止的地方终止”,而不是层层拦截、重复打印、随意吞掉。核心是减少冗余日志、保留原始上下文、集中处理出口。
只在必要层级捕获,中间层优先上抛
业务逻辑层(如 service)一般不主动 try-catch 运行时异常(如 NullPointerException、IllegalArgumentException)。这些异常应暴露出来,由外层统一拦截。若需补充上下文,可包装成更明确的业务异常,但必须传入原始异常作为 cause:
- ✅ 正确:throw new BusinessException("订单创建失败", e);
- ❌ 错误:catch (Exception e) { logger.error("出错了"); } —— 没有日志内容、没堆栈、没再抛出
禁用空 catch 块,哪怕“觉得可以忽略”
空 catch 不仅掩盖问题,还导致故障无法追溯。即使确认某异常可忽略(比如某些第三方 SDK 的偶发回调失败),也必须显式记录原因并保留堆栈:
- ✅ 正确:// 忽略网络探测超时,因属非关键路径,不影响主流程
catch (TimeoutException e) { log.debug("心跳探测超时,跳过处理", e); } - ❌ 错误:catch (TimeoutException e) { }
统一出口拦截,不做多层日志轰炸
把日志记录收口到最外层(如 Spring 的 @ControllerAdvice 或 WebFilter),避免 Controller → Service → DAO 每层都 log.error。这样能保证:
立即学习“Java免费学习笔记(深入)”;
- 每条异常只记录一次完整堆栈;
- 错误响应体格式统一(如含 error code、message、traceId);
- 便于接入监控系统(如 ELK、SkyWalking)做聚合分析。
区分异常类型,按需处理而非一锅端
不要用 catch (Exception e) 捕获一切。应分情况对待:
- 受检异常(IOException、SQLException):通常需显式处理或转换为业务异常;
- 运行时异常(NullPointerException、IllegalStateException):多数反映代码缺陷,不应静默,而应修复或转为带语义的异常;
- Error 及其子类(OutOfMemoryError):绝不捕获,JVM 问题需靠运维和调优解决。


















