Menu
CoddyTech

try-with-resources 模式

本课属于 Coddy Java 学习旅程的“面向对象编程”单元。第 62 课,共 87 课。

使用文件流或数据库连接等资源时,完成后必须始终将它们关闭。否则可能导致内存泄漏。传统方法会使用 finally 块,但这种写法冗长且容易出错。

Java 的 try-with-resources 语句会在代码块执行完毕时自动关闭资源。任何实现了 AutoCloseable 接口的类都可以使用这种语法:

// 传统方式:代码冗长,还有风险
BufferedReader reader = null;
try {
    reader = new BufferedReader(new FileReader("file.txt"));
    String line = reader.readLine();
} finally {
    if (reader != null) {
        reader.close();  // 这里也可能抛出异常!
    }
}

// Try-with-resources:简洁又安全
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
    String line = reader.readLine();
}  // 在这里自动关闭

你可以用分号分隔多个资源来进行管理。它们会按照声明顺序的相反顺序关闭:

try (FileReader fr = new FileReader("input.txt");
     BufferedReader br = new BufferedReader(fr)) {
    // 使用这两个资源
}  // 先关闭 br,再关闭 fr

要创建自己的自动关闭类,请实现 AutoCloseable 接口及其唯一的 close() 方法:

public class DatabaseConnection implements AutoCloseable {
    public DatabaseConnection() {
        System.out.println("Connection opened");
    }
    
    @Override
    public void close() {
        System.out.println("Connection closed");
    }
}

这种模式可确保资源始终得到妥善释放,即使处理过程中发生异常也不例外。

challenge icon

挑战

简单

让我们构建一个资源管理系统,展示 try-with-resources 的强大之处!你将创建可自动关闭的自定义类,模拟管理连接和会话,确保它们始终得到妥善清理。

你将把代码分成三个文件:

  • Connection.java:创建一个模拟数据库连接的类。你的 Connection 类应实现 AutoCloseable。它需要一个用于存储连接名称(String)的私有字段,一个接收名称并打印 [name] connection opened 的构造函数,一个打印 Executing on [name]: [sql] 的 query(String sql) 方法,以及必需的 close() 方法,该方法会打印 [name] connection closed。
  • Session.java:再创建一个模拟用户会话的可自动关闭类。你的 Session 类也应实现 AutoCloseable。添加一个用于存储用户名(String)的私有字段,一个接收用户名并打印 Session started for [username] 的构造函数,一个打印 [username] performed: [action] 的 performAction(String action) 方法,以及一个打印 Session ended for [username] 的 close() 方法。
  • Main.java:把资源管理系统组合起来!你将收到三个输入:连接名称、用户名和要执行的操作。

    首先,演示单个资源的管理。打印 === Single Resource ===,然后使用 try-with-resources 创建一个名称为给定值的 Connection。在 try 代码块中,调用 query("SELECT * FROM users")。代码块结束时,连接应自动关闭。

    接下来,演示多个资源的管理。先打印一个空行,然后打印 === Multiple Resources ===。使用 try-with-resources,同时声明一个 Connection(使用相同的名称)和一个 Session(使用用户名),并按此顺序用分号分隔。在 try 代码块中,先对连接调用 query("INSERT INTO logs"),然后对会话调用 performAction,并传入输入的操作。留意资源关闭的顺序是相反的!

你将依次收到三个输入:连接名称(String)、用户名(String)和操作(String)。

注意,try-with-resources 无需显式调用 close,也无需使用 finally 代码块。即使发生异常,也能确保资源关闭;多个资源则会按声明顺序的相反顺序关闭!

亲手试试

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String connectionName = scanner.nextLine();
        String username = scanner.nextLine();
        String action = scanner.nextLine();
        
        // === Single Resource ===
        // TODO: 打印标题
        // TODO: 使用 try-with-resources 创建 Connection
        // TODO: 在 try 代码块中调用 query("SELECT * FROM users")
        
        // === Multiple Resources ===
        // TODO: 打印一个空行和标题
        // TODO: 使用 try-with-resources 同时管理 Connection 和 Session
        // TODO: 在 try 代码块中:
        //       - 在连接上调用 query("INSERT INTO logs")
        //       - 在会话上调用 performAction,传入输入的 action
        
    }
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Java 在线编译器