只有实现了AutoCloseable接口的类才能用于try-with-resources,如FileInputStream、BufferedReader等;自定义资源需显式实现该接口,多资源用分号隔开且逆序关闭,close异常会被抑制而非覆盖主异常。

哪些资源能用 try-with-resources?
只有实现了 AutoCloseable 接口的类才能用于 try-with-resources。常见如 FileInputStream、BufferedReader、Connection、PreparedStatement,但注意:不是所有“带 close() 方法”的类都满足——比如早期的 java.sql.ResultSet(JDBC 4.0+ 才实现 AutoCloseable)。
自定义资源必须显式 implements AutoCloseable,且重写 close() 方法;若只实现 Closeable(它是 AutoCloseable 的子接口),也完全合法。
容易踩的坑:
-
close()抛出异常时,会被抑制(suppressed),不会中断主异常流程,但可能被忽略——调试时需调用getSuppressed()查看 - 多个资源按声明**逆序**关闭(先声明的后关),这点影响资源依赖顺序,比如先开文件再开缓冲流,就得让缓冲流在前声明,确保它先关
try-with-resources 声明多资源时的语法细节
多个资源用分号隔开,每个资源声明是独立语句,不能省略类型和变量名:
立即学习“Java免费学习笔记(深入)”;
try (FileInputStream fis = new FileInputStream("a.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(fis))) {
// ...
}
错误写法包括:
- 漏掉分号:
try (A a = new A() B b = new B())→ 编译失败 - 混用变量声明与赋值:
try (var a = new A(); var b = new B())→ Java 11+ 支持var,但必须每个资源单独用var,且仍需分号 - 把 if 或其他语句塞进括号里:
try (if (x) new A())→ 不合法,括号内只接受资源声明语句
与传统 try-catch-finally 对比:异常处理差异
传统写法中,close() 放在 finally 块里,若它抛异常且 try 块也抛异常,后者会被覆盖;而 try-with-resources 会保留 try 块的异常,并将 close() 异常作为 suppressed 异常附加。
这意味着:
- 你不能靠 catch 捕获
close()抛出的异常——它不会向上冒泡到 catch 子句 - 如果想主动处理关闭异常,得在资源类的
close()内部捕获并记录,而不是依赖外部 try-catch - 测试时若 mock 资源,务必让
close()方法可抛异常,否则无法覆盖 suppressed 异常路径
非 AutoCloseable 类怎么兼容 try-with-resources?
比如某个老库的连接类只有 disconnect() 方法,没实现 AutoCloseable,又不想改源码,可以包装一层:
class DisconnectableWrapper implements AutoCloseable {
private final LegacyConnection conn;
DisconnectableWrapper(LegacyConnection conn) { this.conn = conn; }
public void close() { conn.disconnect(); }
}
然后这样用:
try (DisconnectableWrapper w = new DisconnectableWrapper(oldConn)) {
// ...
}
注意点:
- 包装类本身不能抛受检异常(
close()声明只能是throws Exception或更宽泛的,但通常建议吞掉或转为运行时异常) - 别在 wrapper 的
close()里做耗时或阻塞操作,否则拖慢整个 try 块退出 - 如果原方法可能重复调用导致状态错乱,wrapper 需加标记位防止多次 disconnect
真正难处理的是那些 close 行为不可逆、且没有状态检查的老资源——这时候 try-with-resources 只是语法糖,实际安全还得靠业务逻辑兜底。










