Java异常处理需精准捕获、完整记录栈追踪、妥善包装异常链、finally仅做清理。应按业务捕获具体子类异常,日志必须传入Throwable对象,敏感信息需脱敏,包装异常时须传递cause,优先使用try-with-resources。

Java异常处理不是“加个try-catch就完事”,关键在于让异常既可定位、又可恢复、还不拖慢系统。栈追踪(stack trace)是异常自带的“事故现场地图”,但只有配合规范的处理逻辑,这张图才能真正帮上忙。
捕获要具体,别用Exception一锅端
用catch (Exception e)看似省事,实则掩盖问题本质。比如网络调用失败时,可能是超时(SocketTimeoutException)、连接拒绝(ConnectException)或解析错误(JsonParseException),每种原因对应不同策略:重试、降级、告警。笼统捕获会丢失类型语义,也让后续日志分类、监控告警失效。
- 优先按业务场景捕获最具体的子类异常
- 多个相关异常可用Java 7+的多异常语法:
catch (IOException | SQLException e) - 若需兜底,放在最后且明确记录——例如
catch (Exception e) { log.warn("未预期异常,类型:{}", e.getClass().getName(), e); }
日志必须带完整栈追踪,且保留原始上下文
只记录e.getMessage()等于删掉了最关键线索。栈追踪包含异常发生位置、调用链路、各层变量状态(若启用了调试信息),是定位根因的核心依据。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 使用SLF4J等日志框架时,始终把
Throwable作为最后一个参数传入,如log.error("订单支付失败,订单号:{}", orderId, e) - 避免在日志中拼接字符串掩盖异常对象:
log.error("失败:" + e)→ 丢失堆栈 - 敏感字段(如密码、token)需脱敏后再写入日志,但堆栈本身不包含业务数据,无需过滤
异常链不能断,包装时务必传递cause
当需要将底层异常转换为业务异常(例如把SQLException转成InventoryLockFailedException),若不保留原始异常,排查时就会断在中间层,再也看不到数据库连接超时还是SQL语法错。
立即学习“Java免费学习笔记(深入)”;
- 构造新异常时,使用带
cause的构造器:new InventoryLockFailedException("库存锁定失败", sqlEx) - 不要用
initCause()补救——它仅在构造器未设cause时才安全,易出错 - 日志输出时,框架会自动展开整个异常链;监控系统(如ELK、SkyWalking)也能据此聚合归因
finally里只做清理,绝不return或抛新异常
finally的本职是确保资源释放,但它执行时机特殊:在try或catch中的return语句已确定返回值后、实际返回前执行。此时若finally也return,会直接覆盖原结果;若抛出新异常,则吞掉原有异常。
- 关闭流、连接、锁等操作放
finally,但仅调用close()等无返回值方法 - 推荐用
try-with-resources替代手动finally,编译器自动生成安全清理代码 - 若清理过程可能失败(如
close()抛IOException),应单独捕获并记录,而非向上抛出

















