应优先捕获具体异常子类,如IOException、SQLException;避免catch(Exception e)吞没RuntimeException;用try-with-resources替代finally中close()以防异常屏蔽;勿用异常做流程控制;多catch须按子类到父类顺序排列。

try-catch 块里到底该 catch 哪个异常类型
Java 强制你区分「检查型异常(checked)」和「非检查型异常(unchecked)」,但很多人一上来就 catch (Exception e),结果吞掉关键错误,或者漏掉本该处理的业务异常。
真实场景中,你应该优先捕获具体子类,比如 IOException、SQLException,而不是笼统的 Exception。因为:
-
Exception会捕获到RuntimeException(比如NullPointerException),这类通常不该被静默吞掉,而是该暴露出来定位问题 - 不同子类代表不同恢复策略:文件读取出错可能重试,数据库连接失败可能降级,直接统一处理反而掩盖意图
- 编译器对 checked 异常有强制要求——不声明不捕获就过不了编译,这是设计约束,不是麻烦
示例:读文件时,写成 catch (FileNotFoundException e) 比 catch (IOException e) 更精准;如果还要处理权限问题,再加一个 catch (SecurityException e)。
finally 里放资源关闭代码,但别在其中 throw 新异常
很多人在 finally 块里调用 close(),却没注意它自己也可能抛出 IOException,导致原始异常被覆盖——这就是「异常屏蔽(exception masking)」。
立即学习“Java免费学习笔记(深入)”;
正确做法是:用 try-with-resources(Java 7+),或在 finally 中做空判 + 吞掉关闭异常:
- 优先用
try (FileInputStream fis = new FileInputStream("a.txt")) { ... },JVM 自动调用close(),且不会掩盖主异常 - 如果必须手写
finally,记得判空:if (fis != null) try { fis.close(); } catch (IOException ignored) {} - 不要在
finally里写throw new RuntimeException(),这会让前面catch的逻辑彻底失效
try-catch 不是万能兜底,别用来替代条件判断
常见反模式:用 Integer.parseInt() 加 catch (NumberFormatException) 来验证字符串是否为数字。性能差、语义错、堆栈污染。
原因很直接:
- 异常创建开销大,比正则或字符遍历慢一个数量级
- 异常表示「意外状况」,而字符串格式校验是预期中的输入校验逻辑
- 频繁触发会导致 JVM JIT 对该方法优化降级,影响整体性能
替代方案:用 Apache Commons Lang 的 NumberUtils.isDigits(),或自己写个简单循环判断每个字符是否为数字(注意负号和空字符串)。
多 catch 顺序错了会编译失败
Java 要求子类异常必须写在父类异常之前,否则报错:error: exception XXX has already been caught。
比如下面这段代码无法通过编译:
try {
// ...
} catch (Exception e) {
// ...
} catch (IllegalArgumentException e) { // ❌ 编译错误
// ...
}
因为 IllegalArgumentException 是 Exception 的子类,上面已经捕获了所有 Exception 及其后代。
必须反过来写:
catch (IllegalArgumentException e)catch (IOException e)-
catch (Exception e)—— 放最后,仅作兜底
这个限制看似死板,实则是帮你提前发现逻辑漏洞:如果你真需要同时处理多个具体异常,说明它们的处理路径不同,混在一起反而危险。
异常处理最易被忽略的点,其实是「什么时候不该 catch」——比如在 service 层吞掉数据库异常却不记录日志,或者把 RuntimeException 包装成 success/fail 返回码返回给前端。这些不是语法问题,而是职责边界模糊的结果。








