不能忽略关闭异常,因其常发生在资源损坏、连接断开等不可恢复状态,静默吞掉会导致资源泄漏、主异常被覆盖、多资源漏关;须用单独try-catch、不throw不return、至少warn日志留痕。

资源关闭时的异常不能“忽略”,只能“安全处理”——核心是不掩盖主异常、不中断清理流程、至少留痕。
为什么不能真忽略
关闭异常(如 IOException 或 SQLException)常发生在资源已损坏、连接已断开、文件系统只读等不可恢复状态。若静默吞掉,会导致:
- 调用方完全不知资源未释放,可能引发后续 NullPointerException 或句柄泄漏
- 原始业务异常被覆盖,问题定位困难
- 多个资源中一个关崩,其余也跟着漏关
安全处理的三原则
不是不处理,而是用对方式:
- 不 throw,不 return:finally 或 try-with-resources 的 close() 阶段绝不抛新异常或提前退出
- 每个 close() 单独 try-catch:避免一个失败阻断整个清理链
-
至少记录 warn 级日志:如
logger.warn("关闭数据库连接失败", e),保留堆栈供排查
两种主流场景的写法
① 使用 try-with-resources(推荐)
JVM 自动调用 close(),异常会作为 suppressed exception 附加到主异常上:
try (Connection conn = ds.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
// 业务逻辑
} catch (SQLException e) {
// 主异常仍是这里抛出的 e
// 可通过 e.getSuppressed() 查看所有 close 失败详情
logger.error("查询失败", e);
}
② 手动管理(需判空 + 分离捕获)
适用于资源跨方法传递、无法用 try-with-resources 的场景:
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = ds.getConnection();
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
// ...
} finally {
if (rs != null) {
try { rs.close(); } catch (SQLException e) {
logger.warn("关闭 ResultSet 失败", e);
}
}
if (ps != null) {
try { ps.close(); } catch (SQLException e) {
logger.warn("关闭 PreparedStatement 失败", e);
}
}
if (conn != null) {
try { conn.close(); } catch (SQLException e) {
logger.warn("关闭 Connection 失败", e);
}
}
}
可选增强:用工具类兜底
Apache Commons IO 提供了 IOUtils.closeQuietly() 和 JDBCUtils.closeQuietly(),内部已做判空和异常捕获,语义清晰、代码简洁:
finally {
IOUtils.closeQuietly(is);
JDBCUtils.closeQuietly(rs, ps, conn);
}
注意:它不记录日志,如需留痕,仍建议自行包装或改用带 logger 的封装。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











