【Java】try-with-resources语法糖
try-with-resources 是 Java 7 引入的一个语法糖,旨在简化资源管理(如文件流、数据库连接、网络连接等)的代码编写,并确保资源在使用后能够正确关闭,从而避免资源泄漏。
以下是关于它的详细解析:
1. 什么是“资源”?
在 Java 中,资源是指实现了 java.lang.AutoCloseable 接口(或其子接口 java.io.Closeable)的对象。这些对象在使用完毕后必须显式关闭,否则会占用系统资源(如文件句柄、数据库连接池连接等)。
常见的资源类包括:
InputStream/OutputStreamReader/WriterConnection/Statement/ResultSet(JDBC)Socket
2. 为什么使用它?(对比传统 try-catch-finally)
传统写法 (Java 7 之前)
在 Java 7 之前,我们需要在 finally 块中手动关闭资源。这不仅代码冗长,而且容易出错(例如,在关闭资源时也需要处理异常,甚至可能掩盖 try 块中抛出的异常)。
// 传统写法:冗长且繁琐
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();
}
}
}
try-with-resources 写法 (Java 7+)
使用 try-with-resources,资源会在 try 块执行完毕后自动关闭,无需手动编写 finally 代码块。
// 现代写法:简洁优雅
// 在 try 的括号中声明和初始化资源
try (FileInputStream fis = new FileInputStream("test.txt")) {
// 读取数据...
// 无需手动关闭,系统会自动调用 close() 方法
} catch (IOException e) {
e.printStackTrace();
}
3. 核心原理
编译器在编译 try-with-resources 代码时,会自动将其扩展为包含 finally 块的代码,并调用资源的 close() 方法。
关键点:
- 接口要求:资源必须实现
AutoCloseable接口(Closeable继承自AutoCloseable)。 - 自动关闭:无论
try块是正常结束还是抛出异常,资源都会在退出try块时自动关闭。 - 关闭顺序:如果声明了多个资源,它们的关闭顺序与声明顺序相反(后声明的先关闭)。
4. 声明多个资源
可以在 try 的括号中用分号 ; 分隔多个资源。
import java.io.*;
public class TryWithResourcesExample {
public static void main(String[] args) {
// 自动关闭资源
try (FileReader fr = new FileReader("example.txt");
BufferedReader br = new BufferedReader(fr)) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
try (FileInputStream fis = new FileInputStream("input.txt");
FileOutputStream fos = new FileOutputStream("output.txt")) {
// 处理逻辑...
} catch (IOException e) {
e.printStackTrace();
}
// 执行完毕后,fos 会先关闭,然后 fis 关闭
5. 异常处理的改进(异常抑制)
这是 try-with-resources 一个非常重要但容易被忽视的特性。
场景:
假设 try 块中的代码抛出了异常 A,紧接着在自动关闭资源时,close() 方法也抛出了异常 B。
- 传统写法:
try块中的异常 A 会被close()抛出的异常 B 覆盖(Suppressed),程序最终只能看到异常 B,导致排查困难。 - Try-with-resources:Java 会保留
try块中抛出的异常 A 作为主异常,并将close()抛出的异常 B 通过Throwable.addSuppressed()方法附加到 A 上。调试时,你可以看到所有被抑制的异常,这对于排错非常有帮助。
try (MyResource res = new MyResource()) {
throw new Exception("Try block exception");
} catch (Exception e) {
// e 是 "Try block exception"
// e.getSuppressed() 包含了 close() 方法抛出的异常
}
6. Java 9 的改进
在 Java 7 和 8 中,资源必须在 try 的括号内声明和初始化。
从 Java 9 开始,如果资源已经在 try 块外部声明并且是 final 或 effectively final(事实不可变)的,可以直接将其放入括号中,无需重新声明。
// Java 9+ 写法
FileInputStream fis = new FileInputStream("test.txt");
// 直接传入已有的变量
try (fis) {
// 使用 fis
} catch (IOException e) {
e.printStackTrace();
}
总结
优点:
- 代码简洁:消除了大量的
finally样板代码。 - 安全性:确保资源一定会被关闭,防止内存泄漏或连接泄漏。
- 调试友好:通过“异常抑制”机制,保留了业务逻辑抛出的原始异常,而不是被关闭资源的异常覆盖。
建议:
在任何需要管理实现了 AutoCloseable 接口的资源时,首选使用 try-with-resources,而不是传统的 try-catch-finally。
更多推荐



所有评论(0)