Java 7+中try-with-resources要求资源实现AutoCloseable接口,必须在try括号内声明初始化,多资源按声明逆序关闭,close异常会被抑制,未实现该接口的类(如Java 19前Scanner)不可用。

Java 7+ 中 try-with-resources 的基本写法
只要资源实现了 AutoCloseable(比如 InputStream、OutputStream、Connection、Statement),就能用 try-with-resources 自动关闭,不用手写 finally 块。
常见错误是把资源声明在 try 外部再传入——这样不会触发自动关闭:
FileInputStream fis = new FileInputStream("a.txt");
try {
// ❌ fis 不在 try-with-resources 括号里,不会被自动关闭
} catch (IOException e) { ... }
正确写法必须在括号内声明并初始化:
try (FileInputStream fis = new FileInputStream("a.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(fis))) {
String line = reader.readLine();
} catch (IOException e) {
// 异常处理
}
- 多个资源用分号隔开,关闭顺序与声明顺序相反(后声明的先关)
- 即使
try块中抛出异常,所有资源仍会被依次调用close() - 如果
close()本身也抛异常,它会被抑制(suppressed),主异常仍被抛出,可通过getSuppressed()获取
哪些类能用 try-with-resources?
不是所有“流”都默认支持。关键看是否实现 AutoCloseable 接口:
- ✅ 标准 I/O 类:
FileInputStream、BufferedWriter、ObjectOutputStream - ✅ JDBC 类:
Connection、PreparedStatement、ResultSet(JDBC 4.0+) - ❌
java.util.Scanner虽有close(),但不实现AutoCloseable(直到 Java 19 才修复) - ❌ 自定义类若没显式实现
AutoCloseable,就不能直接用于try-with-resources
检查方式很简单:javap -s java.io.FileInputStream 看签名里有没有 implements java.lang.AutoCloseable。
try-with-resources 和手动 finally 关闭的区别
表面看只是语法糖,但行为差异很实际:
- 手动
finally容易漏关或重复关(比如if (fis != null) fis.close()写错条件) - 多个资源时,手动写要嵌套
try-catch防止一个close()失败影响后续关闭;try-with-resources内部已处理好抑制异常逻辑 - 性能上无差异,编译后仍是
finally块 + 字节码级资源清理 - 兼容性:仅限 Java 7+;若项目还跑在 Java 6,必须用传统方式
注意:如果资源的 close() 方法抛出未检查异常(如 RuntimeException),它会中断当前关闭流程,但其他资源仍会继续关闭——这点和很多人直觉相反。
自定义资源类如何支持 try-with-resources
只需让类实现 AutoCloseable,并重写 close() 方法:
public class MyResource implements AutoCloseable {
private final String name;
public MyResource(String name) {
this.name = name;
System.out.println(name + " opened");
}
@Override
public void close() throws Exception {
System.out.println(name + " closed");
}
}
然后就能这样用:
try (MyResource r1 = new MyResource("r1");
MyResource r2 = new MyResource("r2")) {
// ...
} // 输出:r2 closed → r1 closed
- 务必确保
close()是幂等的(多次调用不报错、不重复释放) - 如果关闭逻辑可能失败,应抛出
Exception(而非RuntimeException),否则抑制机制不生效 - 不要在
close()里吞掉异常;若需日志,记录后仍要 re-throw 或包装为Exception
最常被忽略的是:资源对象本身可能为 null。虽然 try-with-resources 不允许传 null(会直接抛 NullPointerException),但如果你用工厂方法返回可能为 null 的资源,得提前判空,否则 NPE 会掩盖原本的业务异常。










