捕获 Exception 本身不是坏习惯,但不加区分地捕获会掩盖编程错误、干扰异常语义、误吞关键异常并导致日志失真;应优先捕获具体异常,仅在顶层兜底或特殊场景下谨慎使用。
捕获 Exception 本身不是“坏习惯”,但**在不加区分、不加约束地捕获它时,确实会带来显著风险**。关键不在于能不能捕,而在于是否理解它覆盖了什么、是否真的需要处理所有这些异常。
Exception 是除 Error 外所有异常的父类,包括:
IOException、SQLException,编译器强制你处理;NullPointerException、IllegalArgumentException、ArrayIndexOutOfBoundsException,通常反映编程错误或不可恢复状态;用一个 catch (Exception e) 把它们全兜住,等于模糊了“可恢复故障”和“程序缺陷”的边界。
NullPointerException 被静默吞掉,导致后续逻辑错乱或数据损坏;SQLException,Service 层却用 Exception 捕获后转成通用错误码,丢失了数据库连接失败、SQL语法错误等关键上下文;InterruptedException 被捕获却不恢复中断状态,导致线程无法被优雅关闭;e.toString(),不打印堆栈,或统一打成“系统异常”,运维根本看不出是配置缺失还是网络超时。Files.readAllLines() 就明确 catch IOException;调用 JDBC 就分别处理 SQLException 和可能的 SQLTimeoutException;try {
// ...
} catch
(IOException e) {
log.warn("文件读取失败", e);
throw new BusinessException("文件不可用", e);
} catch (IllegalArgumentException e) {
throw new BusinessException("参数非法", e); // 快速失败
}
@ControllerAdvice)中捕获 Exception 是常见且合理的,但必须区分日志级别、返回状态码,并排除已知不应捕获的类型(如 OutOfMemoryError 子类);log.error("意外异常", e),也要保留完整堆栈;避免 catch (Exception e) { } 这类空处理。极少数场景下它是务实选择:
即便如此,也建议加上注释说明原因,并配套监控告警,而不是当作默认模式。