为什么必须正确使用close

在Java开发中,资源管理是保证程序稳定运行的关键,无论是文件、数据库连接、网络Socket还是IO流,这些系统资源都是由操作系统直接管理的,数量有限,若程序在使用后未通过close()方法释放资源,会导致资源泄漏(Resource Leak),轻则降低系统性能,重则引发程序崩溃(如文件句柄耗尽)或数据库连接池溢出,掌握正确的close()用法是Java开发者的必备技能。
传统IO流资源的关闭方式
Java的传统IO流(如FileInputStream、FileOutputStream、BufferedReader等)都实现了Closeable接口,需显式调用close()释放资源,关闭的核心原则是“先开后关,后开先关”,例如使用FileInputStream读取文件后,需确保流被关闭,即使发生异常也不能遗漏。
典型代码如下:
FileInputStream fis = null;
try {
fis = new FileInputStream("test.txt");
// 读取文件操作
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close(); // 关闭流
} catch (IOException e) {
e.printStackTrace();
}
}
}
关键点:
- finally块确保关闭:无论是否发生异常,finally块中的close()都会执行,避免资源泄漏。
- null检查:防止流对象未初始化(如文件不存在)时调用close()抛出NullPointerException。
- 异常处理:close()本身可能抛出IOException,需捕获或声明抛出。
数据库连接的关闭实践
数据库连接(Connection)是典型的重要资源,若未关闭,会导致连接池耗尽,后续请求无法获取连接,JDBC规范要求,Connection、Statement、ResultSet均需显式关闭,且关闭顺序与创建顺序相反(ResultSet → Statement → Connection)。
示例代码:

Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "user", "password");
stmt = conn.createStatement();
rs = stmt.executeQuery("SELECT * FROM users");
// 处理结果集
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (rs != null) try { rs.close(); } catch (SQLException e) { e.printStackTrace(); }
if (stmt != null) try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); }
if (conn != null) try { conn.close(); } catch (SQLException e) { e.printStackTrace(); }
}
优化建议:实际开发中推荐使用连接池(如HikariCP),并通过try-with-resources或框架(如Spring的JdbcTemplate)自动管理连接,减少手动关闭的遗漏风险。
NIO资源的关闭要点
Java NIO(New IO)引入了Channel、Selector等资源,它们同样实现了Closeable接口,但关闭方式与传统IO略有不同,FileChannel通过close()关闭,而Selector需先关闭所有注册的Channel,再关闭自身,否则可能引发资源泄漏。
示例:
FileChannel channel = null;
Selector selector = null;
try {
channel = FileChannel.open(Paths.get("test.txt"), StandardOpenOption.READ);
selector = Selector.open();
channel.register(selector, SelectionKey.OP_READ);
// NIO操作
} catch (IOException e) {
e.printStackTrace();
} finally {
if (channel != null) try { channel.close(); } catch (IOException e) { e.printStackTrace(); }
if (selector != null) try { selector.close(); } catch (IOException e) { e.printStackTrace(); }
}
注意:NIO中的Buffer不需要手动关闭,它由JVM垃圾回收管理。
最佳实践:try-with-resources
Java 7引入了try-with-resources语法,实现了资源的“自动关闭”,极大简化了代码并避免泄漏,只要实现了AutoCloseable接口(Closeable继承自AutoCloseable)的资源,均可放在try语句中,JVM会在try块结束时自动调用close(),即使发生异常也能保证释放。
对比传统方式,try-with-resources代码更简洁:

try (FileInputStream fis = new FileInputStream("test.txt");
FileOutputStream fos = new FileOutputStream("output.txt")) {
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
优势:
- 自动关闭:无需手动编写finally块,减少代码量。
- 异常抑制:若try块和close()均抛出异常,close()的异常会被抑制,作为“ suppressed exception”附加到主异常上,便于排查问题。
- 支持多资源:同时声明多个资源时,关闭顺序与声明顺序相反(后声明的先关闭)。
常见错误与避免方法
-
忘记关闭:尤其在有return或异常时,finally块可能被跳过。
解决:优先使用try-with-resources,或确保finally块无条件执行。 -
重复关闭:调用close()后再次调用会抛出IOException。
解决:关闭前检查资源是否已关闭(如if (!fis.isOpen()) fis.close();,注意部分资源需通过isOpen()判断)。 -
关闭顺序错误:例如先关闭Connection再关闭Statement,会导致Statement无法正确释放。
解决:遵循“后创建先关闭”原则,或依赖try-with-resources的自动顺序。
Java资源的关闭是程序健壮性的基石,开发者需根据场景选择合适的方式——传统IO和数据库连接优先用try-with-resources,NIO资源注意关闭顺序,同时避免常见错误,通过规范的资源管理,可有效提升程序性能和稳定性。
















