Java try-with-resources 语句

Java 9 新特性 Java 9 新特性

try-with-resources 是 Java 7 引入的一种简洁的资源管理方式,适用于需要在使用后自动关闭的资源(如文件、数据库连接、网络连接等)。

try-with-resources 能够很容易地关闭在 try-catch 语句块中使用的资源,所谓的资源(resource)是指在程序完成后,必须关闭的对象。

try-with-resources 语句确保了每个资源在语句结束时关闭。

所有实现了 java.lang.AutoCloseable 接口(其中,它包括实现了 java.io.Closeable 的所有对象),可以使用作为资源。

优势:

  • 简化代码:省去手动关闭资源的代码,逻辑更清晰。
  • 减少错误:自动处理资源关闭,避免忘记关闭资源或处理 finally 块中出现的异常。
  • 提升性能:减少资源泄露,节约系统资源。

执行流程

理解 try-with-resources 的核心在于掌握资源的生命周期,以及异常发生时的控制流走向。

基本语法

try-with-resources 语法如下:

try (ResourceType resource = new ResourceType()) {
    // 使用资源
} catch (ExceptionType e) {
    // 处理异常
}

在 try 块中声明的资源会在代码执行完毕后自动关闭,甚至在发生异常时也会安全关闭。

多资源管理(按声明逆序关闭):

try (Connection  conn = DriverManager.getConnection(url);
     Statement  stmt = conn.createStatement();
     ResultSet  rs   = stmt.executeQuery(sql)) {

    while (rs.next()) {
        System.out.println(rs.getString(1));
    }
} catch (SQLException e) {
    e.printStackTrace();
}

关闭顺序:

rs → stmt → conn(声明顺序的逆序)

关闭顺序:多个资源按声明顺序的 逆序 关闭,即后声明的资源先被关闭。这与栈式(LIFO)管理一致,确保依赖关系资源正确释放。

声明 / 初始化 res1 Connection res2 Statement res3 ResultSet 执行 try { ... } 自动关闭(逆序) close res3 ResultSet close res2 Statement close res1 Connection try 块结束

使用 AutoCloseable 接口

任何希望被 try-with-resources 管理的自定义类,只需实现 java.lang.AutoCloseable(或 java.io.Closeable)接口,并实现其 close() 方法。

实例

public class DatabaseConnection implements AutoCloseable {
    private final String url;

    public DatabaseConnection(String url) {
        this.url = url;
        System.out.println("连接已建立: " + url);
    }

    public void query(String sql) {
        System.out.println("执行查询: " + sql);
    }

    @Override
    public void close() {
        System.out.println("连接已关闭: " + url);
        // 释放底层连接资源...
    }
}

// 使用
try (DatabaseConnection db = new DatabaseConnection("jdbc:mysql://localhost/mydb")) {
    db.query("SELECT * FROM users");
} catch (Exception e) {
    e.printStackTrace();
}

输出:

连接已建立: jdbc:mysql://localhost/mydb
执行查询: SELECT * FROM users
连接已关闭: jdbc:mysql://localhost/mydb

异常处理与抑制异常

当 try 块中的业务逻辑 和 close() 同时抛出异常时,try-with-resources 采用特殊策略:业务异常作为主异常向上传播,close() 产生的异常被抑制(Suppressed)并附加在主异常上,可通过 getSuppressed() 获取。

try { ... } 抛出 Exception A (业务异常) close() 抛出 Exception B (关闭异常) 触发 主异常 Exception A 抑制异常 Exception B addSuppressed() catch (Exception e) 捕获主异常 A e.getSuppressed() 获取抑制的 Exception B

实例

try (ResourceWithException res = new ResourceWithException()) {
    res.doSomething();           // 抛出 Exception A(业务异常)
} catch (Exception e) {
    System.out.println("主异常: "  + e.getMessage());
    for (Throwable s : e.getSuppressed()) {   // 获取 Exception B
        System.out.println("抑制异常: " + s.getMessage());
    }
}

输出:

主异常: Exception in doSomething
抑制异常: Exception in close

注意:传统 try-finally 中,finally 块的异常会覆盖业务异常,导致原始错误丢失。try-with-resources 通过抑制异常机制解决了这一经典陷阱。

Java 9 增强:复用外部变量

Java 9 之前,try-with-resources 要求资源必须在 try() 中重新声明。

Java 9 放开了这一限制:只要变量是 effectively final(事实上的常量),可直接在 try() 中引用,无需重复声明。

Java 7 / 8 — 必须重新声明

BufferedReader br = new BufferedReader(new StringReader("hello"));
try (BufferedReader br1 = br) {    // ← 必须声明新变量 br1
    System.out.println(br1.readLine());
}

Java 9+ — 直接复用

BufferedReader br = new BufferedReader(new StringReader("hello"));
try (br) {                           // ← 直接使用 br,无需重声明
    System.out.println(br.readLine());
}

effectively final:变量在初始化之后未再被赋值,编译器会自动视其为 final。不需要显式写 final 关键字。

与 try-finally 对比

维度 try-finally try-with-resources
代码量 多——需手动调用 close() 少——自动关闭
遗漏 close 风险 高——人为疏忽易造成资源泄露 零——编译器保证
多资源嵌套 嵌套层层,可读性差 分号分隔,扁平清晰
异常处理 finally 异常覆盖业务异常 抑制异常机制,信息完整保留
适用范围 任何清理逻辑 实现 AutoCloseable 的资源

try-finally 写法(冗长且易出错)

BufferedReader br = null;
try {
    br = new BufferedReader(new FileReader("data.txt"));
    String line;
    while ((line = br.readLine()) != null) System.out.println(line);
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (br != null) {               // 还需判空
        try { br.close(); }        // close 本身也可能抛异常
        catch (IOException e) { e.printStackTrace(); }
    }
}

try-with-resources 写法(简洁安全)

try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
    String line;
    while ((line = br.readLine()) != null) System.out.println(line);
} catch (IOException e) {
    e.printStackTrace();
}

常见使用场景

分类 常用类/接口
📁 文件 I/O FileInputStream、FileOutputStream、BufferedReader、BufferedWriter、FileChannel
🗄 数据库 Connection、PreparedStatement、Statement、ResultSet、EntityManager
🌐 网络通信 Socket、ServerSocket、HttpURLConnection、InputStream、OutputStream

在处理任何需要显式关闭的资源时,应优先使用 try-with-resources 而非 try-finally。它由编译器保证资源必然被关闭,代码更简洁,异常信息更完整,是 Java 资源管理的最佳实践。

Java 9 新特性 Java 9 新特性