应精准捕获特定受检异常,避免catch(exception e);try块需窄且单一职责;受检异常要语义升维包装;多异常捕获须子类在前;优先用try-with-resources。

别用 catch (Exception e) 包打天下
绝大多数情况下,写 catch (Exception e) 是在掩盖问题,而不是处理问题。它会吞掉 RuntimeException(比如 NullPointerException、ArrayIndexOutOfBoundsException),而这些本该暴露出来修复代码逻辑,不是靠 catch 挡住。
真正该捕获的,是那些你**明确知道如何恢复或降级处理**的受检异常(Checked Exception),比如 IOException、SQLException。
- 如果调用的是
FileInputStream的read(),捕获IOException合理;捕获Exception就过度了 - Spring 中
@Transactional方法里抛出未声明的RuntimeException会触发回滚;但若被外层catch (Exception e)吞掉,事务就“静默失败”了 - 日志里只记
e.toString()而不打e.printStackTrace()或用logger.error("", e),等于丢掉堆栈,查不到根因
try 块要窄,别把无关逻辑塞进去
常见错误:把数据库连接、HTTP 调用、JSON 解析全塞进一个 try,结果异常来了分不清是网络超时还是字段缺失。粒度越粗,定位越慢,恢复策略也越难写。
- 每个
try应只包裹**单一职责的、可能抛出特定异常的语句**,比如单独包一次httpClient.execute() - 不要在
try里做对象构造、状态赋值等非 I/O 非外部依赖操作——它们不该抛受检异常,出了错就是 bug,不该被 catch - 多个外部调用之间用独立
try-catch,方便分别配置重试、熔断或 fallback,比如callPaymentService()和updateOrderStatus()不该共用一个 catch
受检异常没处理干净,编译过不去只是表象
Java 强制你处理 Checked Exception,但很多人用 throws 往上甩,或者加个空 catch 敷衍。这会让异常穿透到更高层,最终在顶层变成不可控的中断或静默失败。
立即学习“Java免费学习笔记(深入)”;
- 如果方法本身无法恢复(比如 DAO 层读不到数据),应将原始异常包装成更上层语义的异常,如
throw new OrderNotFoundException("order_id: " + id, e) - 不要在 service 层
throws IOException—— controller 不关心底层是文件还是网络,它只关心“订单查不到”,语义要逐层升维 - 工具类中遇到
UnsupportedEncodingException这种已成历史的异常(UTF-8 必然支持),可用assert false : e;或直接封装为RuntimeException,不留给调用方困惑
多异常捕获顺序和 finally 里的坑
Java 7+ 支持多异常捕获,但顺序错了会编译失败;finally 看似稳妥,却可能掩盖真实异常。
- 子类异常必须写在父类前面,否则
catch (Exception e)会提前匹配,后面catch (IOException e)永远执行不到 -
try抛出异常 A,finally里又抛出异常 B,则 A 被丢弃,外部只能看到 B——所以finally里只做资源释放,别调用可能抛异常的方法;要用try-catch包住 close - 优先用
try-with-resources,它自动处理close()并保留原始异常(通过addSuppressed),比手写finally更可靠
异常粒度本质是接口契约的体现:捕获太宽,契约就模糊;处理太浅,问题就下沉。真正难的不是语法,而是每次写 catch 前,得想清楚——这里到底该由谁负责、能怎么补救、失败后用户感知是什么。










