Java异常处理的核心是可控、可读、可维护,关键在于精准判断、快速中断与妥善善后;需区分检查型与非检查型异常,合理使用try-with-resources、避免空catch、保留异常链并记录上下文。

Java异常处理不是为了“捕获所有错误”,而是让程序在出错时仍能保持可控、可读、可维护。关键不在多捕,而在准判、快断、善后。
明确区分检查型与非检查型异常
Java把异常分为两类:编译器强制要求处理的检查型异常(如 IOException、SQLException),和运行时才暴露的非检查型异常(如 NullPointerException、IllegalArgumentException)。前者代表外部不确定性(比如文件不存在、网络中断),必须显式处理;后者多源于逻辑缺陷或非法输入,应通过校验提前规避,而非靠 catch 挽救。
- 对 FileInputStream 构造调用,必须 try-catch 或 throws —— 这是语言设计让你直面资源风险
- 对用户传入的字符串做 parseDouble,先 if (str == null || str.trim().isEmpty()) 再转换,比 catch NumberFormatException 更清晰可靠
- 自定义业务异常(如 InsufficientBalanceException)建议继承 RuntimeException,避免污染方法签名
避免空 catch 和“吞掉”异常
写成 catch (Exception e) { } 是健壮性的最大敌人。异常被静默丢弃后,问题不会消失,只会延迟爆发、更难定位。
- 至少记录日志:
logger.warn("支付失败,订单号:{},原因:{}", orderId, e.getMessage()) - 若确实无需干预(如定时任务中某次缓存刷新失败),也应注明理由:
// 忽略:失败不影响主流程,下次重试即可 - 绝不把异常转成 null 返回——调用方无法区分“没查到”和“查崩了”
用 try-with-resources 确保资源释放
文件流、数据库连接、Socket 等资源不关闭,轻则内存泄漏,重则系统句柄耗尽。Java 7 引入的 try-with-resources 语法自动调用 close(),比 finally 手动关更安全简洁。
立即学习“Java免费学习笔记(深入)”;
- 资源类必须实现 AutoCloseable 接口(InputStream、Connection 等均已实现)
- 写法:
try (FileInputStream fis = new FileInputStream("data.txt")) { ... },fis 作用域结束自动关闭 - 多个资源用分号分隔:
try (Connection conn = ds.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ... }
异常链与上下文信息要真实有用
抛出新异常时,保留原始原因(cause),并补充当前层的关键上下文,方便逐层追溯。
- 不要只写
throw new ServiceException("操作失败")—— 丢失堆栈和根因 - 推荐写法:
throw new ServiceException("更新用户 " + userId + " 的邮箱失败", e) - 日志中打印异常时用
logger.error(msg, e)(而非e.printStackTrace()),确保完整堆栈入库 - 对外 API 返回错误码和简明提示,但服务端日志必须含 traceId 和完整异常链


















