try-with-resources 细节
Java 7 引入的 try-with-resources,平时就是 try (FileInputStream fis = new FileInputStream("a.txt")),很多人也就用到这层。但有些细节值得捋清楚。
一、基本用法
java
// 实现 AutoCloseable 接口就行
try (BufferedReader reader = new BufferedReader(
new FileReader("a.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} // 自动调用 close()不用写 finally,也不用担心 close() 漏写。
二、 suppress 异常:close() 抛异常了怎么办
这是很多人没想过的问题。如果 try 块和 close() 都抛异常,谁先谁后?
java
try (MyResource r = new MyResource()) {
r.doSomething(); // 抛出 IOException
} // close() 也抛出 IOException实际行为:try 块的异常会被抛出来,close() 的异常会被 suppress。
怎么拿到被 suppress 的异常?
java
} catch (IOException e) {
// 主异常
System.out.println("主异常:" + e.getMessage());
// 被 suppress 的异常
Throwable[] suppressed = e.getSuppressed();
for (Throwable t : suppressed) {
System.out.println("被吞掉的:" + t.getMessage());
}
}编译器会自动把 close() 的异常加到 suppress 列表里,不会让主异常丢失。
三、多资源声明顺序
可以同时声明多个资源:
java
try (InputStream is = new FileInputStream("a.txt");
OutputStream os = new FileOutputStream("b.txt")) {
is.transferTo(os);
}关闭顺序:先声明的后关闭,跟 finally 的顺序相反。
java
// 实际执行顺序
// 1. is.transferTo(os) // 先用
// 2. os.close() // 后关闭
// 3. is.close() // 先关闭为什么这样设计?后声明的可能依赖先声明的,比如 OutputStream 依赖 InputStream 的数据,先关 InputStream 可能导致数据没刷完。
四、变量必须直接声明
java
// 这样不行
MyResource r = new MyResource();
try (r) { } // 编译错误
// 正确写法
try (MyResource r = new MyResource()) { }原因:编译器要在字节码层面插入资源初始化逻辑,变量声明必须紧贴着 try。
如果想在 try 外拿到这个资源做其他操作:
java
MyResource r = new MyResource();
try (r) {
r.use();
}
// r 已经 close 了,不能再用五、catch 和 finally 还能用
try-with-resources 不影响加 catch 和 finally:
java
try (InputStream is = new FileInputStream("a.txt")) {
// 正常业务
} catch (IOException e) {
// 处理异常
} finally {
// 额外清理逻辑(资源已经在 try 声明里自动关了)
}六、返回值会吞异常吗
java
static String test() {
try (MyResource r = new MyResource()) {
return r.read(); // 这里抛异常
} // close() 也抛异常
}返回的是 try 块的返回值,close() 的异常被 suppress 了。如果想拿到 close() 的异常,照样用 getSuppressed()。
七、常见错误
错误1:以为 try 块抛异常就不会 close
java
try (Connection conn = dataSource.getConnection()) {
throw new RuntimeException("业务异常");
} // 一样会 close()错误2:把普通对象放进 try
java
// 编译通过,但毫无意义
try (new Object()) { } // Object 没实现 AutoCloseable错误3:关了还想用
java
try (BufferedReader br = new BufferedReader(...)) {
return br.readLine();
}
// 返回时已经 close 了,调用方拿到的 Reader 已经废了总结
| 细节 | 说明 |
|---|---|
| close() 异常会被 suppress | 用 getSuppressed() 拿 |
| 多资源关闭顺序 | 先声明的后关 |
| 变量必须直接声明 | 编译器需要 |
| catch/finally 还能加 | 不冲突 |
| 不会漏 close | 总会执行 |
用 try-with-resources 能少写很多 finally,也更安全,至少不用担心资源泄漏。