Java异常处理应区分受检与非受检异常,避免空catch和吞异常,善用try-with-resources与多异常捕获,统一外提处理逻辑,确保每处异常明确归属、可追溯、易维护。

Java异常处理要保持代码整洁,核心是区分异常类型、避免过度捕获、统一错误处理逻辑,而不是堆砌try-catch或盲目抛出异常。
明确区分受检与非受检异常
Java中Exception分为两类:受检异常(Checked Exception)必须显式处理,如IOException;非受检异常(RuntimeException及其子类)可不强制捕获,如NullPointerException、IllegalArgumentException。
- 受检异常用于预期可能失败且调用方应主动应对的场景(如文件读写、网络请求),不应简单包装成RuntimeException吞掉
- 非受检异常适合表示程序逻辑错误或不可恢复状态(如参数非法、空指针),通常不该被层层catch,而应尽早暴露
- 自定义异常优先继承RuntimeException,除非业务明确要求调用方必须处理(例如支付失败需重试/降级)
避免空catch和“吞异常”
空的catch块(catch (Exception e) {})是最常见的整洁性破坏行为,它让问题静默失效,极大增加排查成本。
- 所有catch必须有明确动作:记录日志、转换异常、重试、返回默认值或向上抛出
- 记录日志时使用e.printStackTrace()是反模式,应使用SLF4J等框架记录带上下文的错误信息,例如:log.error("解析JSON失败,原始数据: {}", rawData, e);
- 不要用e.getMessage()代替异常对象传递,会丢失堆栈和类型信息
善用try-with-resources和多异常捕获
资源管理与异常合并能显著减少样板代码。
立即学习“Java免费学习笔记(深入)”;
- 涉及Closeable资源(如FileInputStream、Connection)一律用try-with-resources,自动关闭,无需finally手动close
- 多个同类异常可合并捕获(Java 7+),例如:catch (IOException | SQLException e),避免重复处理逻辑
- 若不同异常需不同处理,再拆分为独立catch;但不要为每个checked异常写一个单独的try块
异常处理逻辑外提,避免业务代码被污染
高频重复的异常转换、兜底策略(如降级、缓存返回)应封装成工具方法或AOP切面。
- 例如数据库操作统一包装为DataAccessException,屏蔽底层JDBC/MyBatis异常细节
- Web层用@ControllerAdvice统一处理全局异常,返回标准错误响应体,Controller内不再出现大量if-else判断异常类型
- 关键服务调用可封装为SafeCall工具:SafeCall.of(() -> userService.getById(id)).orElse(null),把try-catch收敛到一处
基本上就这些。整洁的异常处理不是少写catch,而是让每处异常都有明确归属、可追溯、易维护。不复杂但容易忽略。










