finally块不自动关资源,需手动确保变量声明在try外、逐个判空并捕获异常关闭、按ResultSet→Statement→Connection顺序执行,且不可throw或return。

finally块本身不关资源,只保证代码执行;能否真正释放连接,取决于你在里面写的关闭逻辑是否完整——重点是变量声明位置、判空、异常捕获和关闭顺序。
变量必须声明在try外,且初始化为null
Connection、PreparedStatement、ResultSet这些对象不能在try里直接声明并赋值,否则finally根本访问不到。正确做法是提前声明并设为null:
- Connection conn = null;
- PreparedStatement stmt = null;
- ResultSet rs = null;
这样后续在finally中才能安全判断和调用close()。
每个close()都要单独判空 + 单独try-catch
rs.close()、stmt.close()、conn.close()可能各自抛SQLException,任何一个没捕获,就会中断整个清理流程。不能共用一个try-catch,也不能省略判空——否则null调用会触发NullPointerException。
立即学习“Java免费学习笔记(深入)”;
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- if (rs != null) { try { rs.close(); } catch (SQLException e) { logger.warn("关闭 ResultSet 失败", e); } }
- if (stmt != null) { try { stmt.close(); } catch (SQLException e) { logger.warn("关闭 Statement 失败", e); } }
- if (conn != null) { try { conn.close(); } catch (SQLException e) { logger.warn("关闭 Connection 失败", e); } }
关闭顺序必须是 ResultSet → Statement → Connection
这是JDBC资源的依赖关系决定的:ResultSet内部持有Statement引用,Statement又依赖Connection。如果先关Connection,再关ResultSet,大概率抛“connection closed”异常。
IO流同理:BufferedInputStream要先关,再关底层FileInputStream。
别在finally里throw或return
即使close()出错,也只记录日志,不要throw新异常,更不要return。否则会覆盖try块里原本的业务异常,导致错误信息丢失,排查困难。
现代项目优先用try-with-resources,它自动处理判空、异常抑制和逆序关闭;但维护老系统或对接非AutoCloseable资源时,这套finally写法仍是刚需。

















