避免过度使用try-catch的核心是区分异常类型、分离控制流与错误处理逻辑;只捕获具体异常(如IOException、JsonProcessingException、SQLException子类),不用Exception/Throwable兜底;禁用异常替代条件判断,应使用nonNull、Optional、containsKey等明确表达意图。

避免过度使用 try-catch 的核心,是区分真正需要捕获的异常类型,把控制流逻辑和错误处理逻辑分开,而不是用异常来“兜底”或替代正常判断。
只捕获具体异常,不捕获 Exception 或 Throwable
捕获太宽泛的异常(如 catch (Exception e))会掩盖本该暴露的问题,也容易误吞业务逻辑错误或编程缺陷(比如 NullPointerException、IllegalArgumentException)。应明确知道这段代码可能抛出什么受检/非受检异常,并针对性处理。
- 例如读文件时,关注 IOException,而不是笼统抓 Exception
- 解析 JSON 时,捕获 JsonProcessingException(Jackson)或 JSONException(org.json),而非 Exception
- 数据库操作优先捕获 SQLException 及其子类(如 SQLTimeoutException),并结合事务语义决定是否重试或回滚
不要用 try-catch 替代条件判断
常见反模式:为避免空指针,写 try-catch 包裹 get() 操作;或为判断 Map 是否含 key,用 try-get + catch NoSuchElementException。这违背了异常的设计初衷——异常用于“异常情况”,不是流程分支。
- 用 Objects.nonNull()、Optional.isPresent()、Map.containsKey() 等明确表达意图
- 集合取值优先用 list.get(i) 前校验 i >= 0 && i ,而非靠 IndexOutOfBoundsException 拦截
- 字符串解析数字前,可用正则或 Character.isDigit() 预筛,而不是依赖 NumberFormatException 流程跳转
异常处理要体现业务语义,不裸 throw 或空 catch
捕获后不做任何处理(空 catch)、或原样 re-throw(throw e),会让调用方无法理解上下文;直接 throw new RuntimeException(e) 又丢失原始堆栈和类型信息。
立即学习“Java免费学习笔记(深入)”;
- 记录日志时,用 log.error("订单支付失败,订单号: {}", orderId, e),保留异常对象以便排查
- 需向上透传时,用 throw new BusinessException("支付超时,请重试", e) 封装,带业务码和可读消息
- 对可恢复异常(如网络抖动),考虑有限重试 + 指数退避,而不是立即失败
利用 try-with-resources 和 Optional 减少手动 try-catch
Java 7+ 的 try-with-resources 自动关闭资源,避免 finally 中冗余的 close() 和嵌套 try-catch;Optional 则把“可能为空”的契约显式化,减少防御性 null 检查和对应异常捕获。
- 文件流、数据库连接、HTTP 客户端等实现 AutoCloseable 的资源,统一用 try-with-resources
- 方法返回 Optional
,调用方用 ifPresent()、orElseThrow() 明确处理空场景,无需自己 try-catch NullPointerException - Stream 操作中,filter/map 等本身不抛受检异常,避免在 lambda 里强行 try-catch,可封装工具方法处理受检异常(如 FunctionWithException)
可读性提升的关键,在于让代码“说什么就是什么”:异常只出现在真有意外发生的地方,且每次捕获都有明确目的和后续动作。少一层无意义的 catch,就多一分逻辑清晰度。










