应避免用try-catch做流程控制,因其性能差且掩盖问题;推荐使用校验方法、Optional、try-with-resources及具体异常捕获,并合理设计自定义异常与事务一致性。

不要用 try-catch 做流程控制
Java 中最常被误用的异常机制,就是把 try-catch 当成 if-else 用。比如用 NumberFormatException 捕获来判断字符串是否为数字,或靠 NullPointerException 触发来跳过空值处理——这不仅慢,还掩盖真实问题。
JVM 抛异常是重量级操作:要填充栈轨迹、实例化异常对象、触发异常表查找。一次 catch 的开销通常是普通分支的 10–100 倍。
- ✅ 正确做法:用
String.matches("\\d+")或Integer.parseInt()前先校验格式 - ✅ 空值检查优先用
Objects.nonNull(obj)或 Optional 链式调用 - ❌ 错误示范:
try { Integer.valueOf(str); return true; } catch (NumberFormatException e) { return false; }
try-with-resources 是唯一推荐的“语法糖式”异常处理
当资源(InputStream、Connection、FileChannel 等)必须显式关闭时,try-with-resources 不仅简洁,还能保证 close() 在异常发生时仍被执行,且自动抑制多重异常(suppressed exceptions)。
- 资源类必须实现
AutoCloseable接口(JDK 7+ 所有标准 I/O 类都满足) - 多个资源用分号隔开,关闭顺序与声明顺序相反
- 若
try块抛异常,且close()也抛异常,后者会被压制,可通过Throwable.getSuppressed()查看
try (FileInputStream fis = new FileInputStream("a.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(fis))) {
String line = reader.readLine();
} catch (IOException e) {
// 这里捕获的是 try 块主逻辑或 close() 中首个未被压制的异常
}
捕获具体异常类型,避免裸 catch (Exception e)
泛化捕获会吞掉本该由上层处理的异常(如 InterruptedException),或干扰监控系统对错误类型的统计。JVM 不会阻止你写 catch (Throwable t),但生产代码中几乎从不合法。
立即学习“Java免费学习笔记(深入)”;
- 优先捕获最具体的子类:比如
SQLException而非Exception,IllegalArgumentException而非RuntimeException - 对可恢复异常(如网络超时)做重试或降级;对不可恢复异常(如
NoClassDefFoundError)应快速失败并记录堆栈 - 日志中必须打印完整异常:用
log.error("msg", e),而不是log.error("msg: " + e.getMessage())
自定义异常要区分「检查型」和「非检查型」
是否继承 Exception 还是 RuntimeException,本质是在回答:“调用方是否**必须**处理它?”
- ✅ 检查型异常(
extends Exception):业务中明确需要强制处理的场景,例如支付接口返回「余额不足」,调用方必须弹窗提示用户充值 - ✅ 非检查型异常(
extends RuntimeException):程序逻辑错误或外部依赖故障,如参数校验失败、远程服务不可达,通常由全局异常处理器统一响应 HTTP 500 - ⚠️ 注意:自定义异常类名需以
Exception结尾(如InsufficientBalanceException),且建议提供带cause的构造函数
try-catch 语法里暴露,得靠设计时就明确每个异常分支的副作用边界。










