Java try-with-resources
try-with-resources 如何在异常路径上可靠关闭资源,以及抑制异常(suppressed)语义。
#concept
#growing
#dev
#java
[!info] related notes
Java try-with-resources
这个概念为什么出现
文件、套接字、数据库连接都必须关闭。手工 try/finally 易漏、顺序错、二次异常掩盖主异常。try-with-resources 把“用完即关”做成语法,并保留抑制异常信息。
[!abstract] 一句话理解 在
try (...)中声明的AutoCloseable资源会在块结束时按逆序自动close;主异常上可挂载关闭阶段的 suppressed 异常。
[!note] 边界
AutoCloseable接口、资源所有权与生命周期纪律以 资源生命周期与 AutoCloseable 为 canonical;本文只解释 try-with-resources 这套语法机制及 suppressed exception 语义。
最小可运行示例
场景:导出服务写临时报表文件
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
public class ReportExport {
// export:写一行报表。
// 教学点:BufferedWriter 在 try-with-resources 中自动关闭(会 flush)。
static void export(Path path, String line) throws IOException {
try (BufferedWriter w = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
w.write(line);
w.newLine();
} // 此处自动 close
}
public static void main(String[] args) throws Exception {
Path p = Files.createTempFile("report-", ".txt");
try {
export(p, "order=1,ok");
System.out.println(Files.readString(p).trim());
} finally {
Files.deleteIfExists(p); // 文件句柄关闭与删除临时文件是两件事
}
}
}
建议运行:
javac ReportExport.java && java ReportExport
期望输出:
order=1,ok
结合场景再看三个关注点
- 资源需列在 try 括号内才自动管理;Java 9+ 也允许 try(existingResource),引用已声明的 final/effectively final 变量。\
- 多个资源逆序关闭(后开先关)。
- 关闭失败不会默默丢掉主异常(suppressed)。
核心概念与准确模型
1. 语法
try (Resource r = open()) {
use(r);
}
2. 等价直觉
编译器生成 finally 风格关闭,并处理异常抑制。
3. 抑制异常
- body 抛异常时,它通常作为主异常;逆序 close 的异常追加到 getSuppressed()。
- body 正常完成但 close 抛异常时,第一个关闭异常成为主异常,其余关闭异常被抑制。
- 资源按从左到右初始化;后一个初始化失败时,先前已成功初始化的资源仍会逆序关闭,初始化异常作为主异常。\
4. 生效条件
- 资源实现
AutoCloseable/Closeable - try 内声明的资源变量隐式 final;Java 9+ 引用外部变量时要求 final 或 effectively final。\
边界情况与反直觉行为
- 资源初始化结果为 null 时跳过 close;不是允许在 body 内重赋资源变量。\
- 在 body 里手动提前 close 要避免双重语义混乱。
- 依赖关闭顺序的资源要按正确顺序声明。
- 某个构造器内部获取资源后抛错,尚未完成构造的对象不会被 TWR 自动关闭,构造器要自行回滚。
- TWR 不是 JVM 被强制终止、System.exit 或无限阻塞时的清理保证;close 也不自动意味着删除文件或数据落盘。\
常见误区
[!warning] 常见误区:打开资源在 try 外再在 finally close 新代码优先 try-with-resources,减少遗漏。
工程实践
- 对由当前作用域拥有且实现 AutoCloseable 的 IO/JDBC/HTTP 资源使用 TWR;不要提前关闭借用资源或返回给调用方的流。\
- 自定义资源实现
AutoCloseable。 - 测试异常路径是否仍关闭(可用计数包装)。
- 日志打印 suppressed。
本节总结
- 自动关闭 + 异常更安全
- 逆序 close + suppressed
- IO/JDBC 标配
自测题
- 两个资源关闭顺序?
- 为什么还需要
getSuppressed?
参考答案
- 与声明相反:后获取的先关闭。
- 关闭阶段异常不能覆盖主异常,但必须可观测。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| The try-with-resources Statement | 教程 | 语法与 suppressed |
| AutoCloseable | API | 关闭契约 |