java异常处理关键在于系统性思考:区分checked与unchecked异常语义,业务校验抛runtimeexception子类,外部调用用checked异常;自定义异常须保留cause链;优先用try-with-resources管理资源;全局处理器应分级响应而非兜底。

Java异常处理不只是“try-catch-finally”语法的堆砌,关键在于理解异常的本质、分类逻辑与系统设计意图。真正写出健壮代码,需要跳出“捕获即解决”的惯性,转向“何时抛、为何抛、如何传、怎么兜”的系统性思考。
区分 checked 与 unchecked 异常的语义边界
checked 异常(如 IOException、SQLException)代表调用方**本应预见并主动应对**的外部不确定性,强制编译检查是语言层面对契约的约束;unchecked 异常(RuntimeException 及其子类)则反映程序逻辑缺陷或不可恢复的运行时错误(如 NullPointerException、IllegalArgumentException)。混淆二者会导致异常被“吞掉”或过度包装——比如把业务校验失败硬套 IOException,或对空指针做空 catch。
- 业务校验失败(如参数非法、状态不满足)应抛 RuntimeException 子类(如 IllegalArgumentException),而非包装成 checked 异常
- 调用外部服务失败(如 HTTP 超时、数据库连接中断)属于 checked 异常场景,需明确声明或转换为有业务含义的自定义 checked 异常
- 避免在底层 DAO 层直接 throw new RuntimeException(e),应封装为更具体的业务异常(如 UserNotFoundException),保留原始 cause 便于追溯
异常链(cause)不是可选项,而是诊断刚需
异常的根本价值之一是提供上下文线索。丢弃原始异常(e.g. throw new ServiceException("操作失败"))等于抹去堆栈和根因,让问题排查退化为猜谜。正确做法是通过构造函数传递 cause,构建可穿透的异常链。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 所有自定义异常都应提供带 Throwable cause 的构造方法
- catch 后重新抛出时,优先使用 throw new BusinessException("订单创建失败", e),而非 throw new BusinessException("订单创建失败")
- 日志记录时,用 log.error("支付回调处理异常", e),而非 log.error("支付回调处理异常: " + e.getMessage()),确保完整堆栈入库
finally 不等于资源清理,try-with-resources 才是现代标准
手动在 finally 中 close() 流或连接,不仅冗长,还易因 close() 自身抛异常导致前序异常被掩盖。JDK 7+ 的 try-with-resources 语法由编译器自动插入 close 调用,并保障异常压制(suppressed exception)机制生效,是资源管理的事实标准。
- 所有实现 AutoCloseable 的资源(InputStream、Connection、PreparedStatement 等)必须用 try-with-resources
- 若需兼容旧 JDK 或非 AutoCloseable 对象,才考虑手动 finally + null 检查 + 多重 try
- 避免在 try-with-resources 的资源声明中调用可能抛异常的方法(如 new FileInputStream(file)),否则异常会中断资源初始化流程
全局异常处理器 ≠ 异常终结者,而是统一出口与分级响应
@ControllerAdvice 或 Spring Boot 的 @RestControllerAdvice 是统一拦截和格式化异常的入口,但它不替代业务层的异常决策。它的职责是:将不同来源的异常转为一致的响应结构、记录关键日志、触发告警(如严重异常)、屏蔽敏感信息(如数据库错误详情),而非“兜底吃掉”所有异常。
- 不要在全局 handler 中对所有 Exception 做通用 success=false 返回,应按异常类型做差异化处理(如 400 对应参数异常,500 对应系统异常)
- 对业务异常(如 OrderAlreadyPaidException),返回 200 + 业务码,而非 500;对系统级异常,才返回 500 并脱敏 message
- 全局 handler 中仍要 log.error("全局异常捕获", e),确保原始异常进入监控系统,不能只靠前端展示
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










