ZetCode

Java AutoCloseable 接口

最后修改时间:2025 年 4 月 13 日

java.lang.AutoCloseable 接口在 Java 7 中引入,是 try-with-resources 语句的一部分。它表示不再需要时必须关闭的资源。这有助于防止资源泄漏,并使资源管理更加可靠。

AutoCloseable 接口包含一个名为 close 的方法,该方法在使用 try-with-resources 块时会自动调用。 此接口是现代 Java 应用程序中正确资源管理的基础。

AutoCloseable 接口定义

AutoCloseable 接口很简单,只包含一个方法。 任何实现此接口的类都可以与 try-with-resources 一起使用。 如果资源无法正确关闭,则 close 方法可能会抛出异常。

public interface AutoCloseable {
    void close() throws Exception;

该接口的设计非常简单,旨在方便广泛采用。 当 try 块正常退出或通过异常退出时,会自动调用 close 方法。 这样可以确保资源得到正确释放。

基本的 AutoCloseable 实现

此示例显示了 AutoCloseable 的一个简单实现。 我们创建一个自定义资源,在打开和关闭时打印消息。 try-with-resources 语句确保 close 方法自动调用。

Main.java
class SimpleResource implements AutoCloseable {
    private String name;
    
    public SimpleResource(String name) {
        this.name = name;
        System.out.println("Resource '" + name + "' opened");
    }
    
    public void use() {
        System.out.println("Using resource '" + name + "'");
    }
    
    @Override
    public void close() throws Exception {
        System.out.println("Resource '" + name + "' closed");
    }
}


void main() {
    try (SimpleResource resource = new SimpleResource("Test")) {
        resource.use();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

运行时,此程序会输出消息,显示资源的生命周期。 即使发生异常,也会在 try 块之后自动调用 close 方法。 这演示了实现 AutoCloseable 的基本模式。

try-with-resources 中的多个资源

Try-with-resources 可以管理多个 AutoCloseable 资源。 资源按照声明的相反顺序关闭。 此示例显示了在单个 try 块中一起管理的两个资源。

Main.java
class ResourceA implements AutoCloseable {
    public ResourceA() { System.out.println("ResourceA opened"); }
    public void useA() { System.out.println("Using ResourceA"); }
    @Override
    public void close() { System.out.println("ResourceA closed"); }
}

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


void main() {
    try (ResourceA a = new ResourceA();
            ResourceB b = new ResourceB()) {
        a.useA();
        b.useB();
    }
}

输出显示 ResourceB 在 ResourceA 之前关闭,演示了反向关闭行为。 当资源相互依赖时,此顺序非常重要。 try-with-resources 会自动处理所有清理工作。

带有异常处理的 AutoCloseable

AutoCloseable 资源在使用和关闭期间都可能抛出异常。 此示例演示了如何处理此类情况。 try-with-resources 正确管理异常抑制。

Main.java
class ProblematicResource implements AutoCloseable {
    private String name;
    private boolean failOnUse;
    private boolean failOnClose;
    
    public ProblematicResource(String name, boolean failOnUse, boolean failOnClose) {
        this.name = name;
        this.failOnUse = failOnUse;
        this.failOnClose = failOnClose;
    }
    
    public void use() throws Exception {
        if (failOnUse) {
            throw new Exception("Error using " + name);
        }
        System.out.println("Using " + name);
    }
    
    @Override
    public void close() throws Exception {
        if (failOnClose) {
            throw new Exception("Error closing " + name);
        }
        System.out.println("Closed " + name);
    }
}


void main() {
    try (ProblematicResource r1 = new ProblematicResource("R1", false, false);
            ProblematicResource r2 = new ProblematicResource("R2", true, true)) {
        r1.use();
        r2.use();
    } catch (Exception e) {
        System.out.println("Caught exception: " + e.getMessage());
        for (Throwable t : e.getSuppressed()) {
            System.out.println("Suppressed: " + t.getMessage());
        }
    }
}

此示例显示了如何处理资源使用和关闭期间的异常。 主要异常是在 try 块中抛出的异常,而 close 异常作为抑制异常添加。 这样可以保留所有错误信息。

带有文件 I/O 的 AutoCloseable

AutoCloseable 的一个常见用例是文件操作。 此示例显示了实现 AutoCloseable 的自定义文件读取器。 即使发生异常,它也会正确关闭文件资源。

Main.java
class CustomFileReader implements AutoCloseable {
    private BufferedReader reader;
    
    public CustomFileReader(String filePath) throws FileNotFoundException {
        this.reader = new BufferedReader(new FileReader(filePath));
    }
    
    public String readLine() throws IOException {
        return reader.readLine();
    }
    
    @Override
    public void close() throws IOException {
        System.out.println("Closing file reader");
        if (reader != null) {
            reader.close();
        }
    }
}


void main() {
    try (CustomFileReader fr = new CustomFileReader("test.txt")) {
        String line;
        while ((line = fr.readLine()) != null) {
            System.out.println(line);
        }
    } catch (IOException e) {
        System.err.println("Error processing file: " + e.getMessage());
    }
}

这个自定义文件读取器封装了标准的 Java I/O 类,同时实现了 AutoCloseable。 close 方法确保底层的 BufferedReader 被正确关闭。 try-with-resources 块保证了这一点。

数据库连接中的 AutoCloseable

数据库连接是另一种必须正确关闭的关键资源。 此示例演示了一个简化的数据库连接包装器,该包装器实现了 AutoCloseable。 它确保连接始终被释放。

Main.java
import java.sql.*;

class DatabaseConnection implements AutoCloseable {
    private Connection connection;
    
    public DatabaseConnection(String url, String user, String password) 
            throws SQLException {
        this.connection = DriverManager.getConnection(url, user, password);
        System.out.println("Database connection established");
    }
    
    public void executeQuery(String sql) throws SQLException {
        try (Statement stmt = connection.createStatement();
             ResultSet rs = stmt.executeQuery(sql)) {
            while (rs.next()) {
                System.out.println(rs.getString(1));
            }
        }
    }
    
    @Override
    public void close() throws SQLException {
        if (connection != null && !connection.isClosed()) {
            connection.close();
            System.out.println("Database connection closed");
        }
    }
}


void main() {
    String url = "jdbc:mysql://:3306/test";
    String user = "root";
    String password = "password";
    
    try (DatabaseConnection db = new DatabaseConnection(url, user, password)) {
        db.executeQuery("SELECT * FROM users");
    } catch (SQLException e) {
        System.err.println("Database error: " + e.getMessage());
    }
}

此示例显示了正确的数据库资源管理。 外部的 DatabaseConnection 和内部的 Statement/ResultSet 都是 AutoCloseable。 try-with-resources 确保所有资源都以正确的顺序正确关闭。

具有关闭抑制的自定义资源

有时我们希望阻止资源被关闭。 此示例显示了如何实现 AutoCloseable 资源周围的非关闭包装器。 它演示了高级资源管理技术。

Main.java
class SimpleResource implements AutoCloseable {
    private String name;
    
    public SimpleResource(String name) {
        this.name = name;
        System.out.println("Resource '" + name + "' opened");
    }
    
    public void use() {
        System.out.println("Using resource '" + name + "'");
    }
    
    @Override
    public void close() throws Exception {
        System.out.println("Resource '" + name + "' closed");
    }
}

class NonClosableResource<T extends AutoCloseable> implements AutoCloseable {
    private final T resource;
    private final boolean suppressClose;
    
    public NonClosableResource(T resource, boolean suppressClose) {
        this.resource = resource;
        this.suppressClose = suppressClose;
    }
    
    public T getResource() {
        return resource;
    }
    
    @Override
    public void close() throws Exception {
        if (!suppressClose && resource != null) {
            resource.close();
        }
    }
}


void main() {
    try (NonClosableResource<SimpleResource> wrapper = 
            new NonClosableResource<>(new SimpleResource("Protected"), true)) {
        wrapper.getResource().use();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

这个通用包装器可以保护任何 AutoCloseable 资源免于被关闭。 关闭抑制标志确定是否调用底层资源的 close 方法。 此模式对于共享资源很有用。

带有 Lambda 表达式的 AutoCloseable

Lambda 表达式可以与 AutoCloseable 一起使用,以实现更简洁的资源管理。 此示例显示了使用 AutoCloseable 资源的功能性方法。

Main.java
class SimpleResource implements AutoCloseable {
    private String name;

    public SimpleResource(String name) {
        this.name = name;
        System.out.println("Resource '" + name + "' opened");
    }

    public void use() {
        System.out.println("Using resource '" + name + "'");
    }

    @Override
    public void close() throws Exception {
        System.out.println("Resource '" + name + "' closed");
    }
}

class ResourceUser {
    void withResource(Consumer<SimpleResource> consumer)
            throws Exception {
        try (SimpleResource resource = new SimpleResource("Lambda")) {
            consumer.accept(resource);
        }
    }
}


void main() {
    try {
        ResourceUser resourceUser = new ResourceUser();
        resourceUser.withResource(resource -> {
            System.out.println("Inside lambda");
            resource.use();
        });
    } catch (Exception e) {
        e.printStackTrace();
    }
}

此示例演示了 AutoCloseable 资源的功能包装器。 withResource 方法处理创建和清理,而 lambda 表达式专注于资源使用。 此模式减少了样板代码。

来源

Java AutoCloseable 接口文档

在本教程中,我们探讨了 AutoCloseable 接口及其与 try-with-resources 的使用。 正确的资源管理对于可靠的 Java 应用程序至关重要。 AutoCloseable 提供了一种干净、异常安全的方式来处理资源。

作者

我叫 Jan Bodnar,是一位经验丰富的程序员。 我从 2007 年开始撰写编程文章,至今已创作了 1,400 多篇文章和 8 本电子书。 凭借超过八年的教学经验,我致力于分享我的知识并帮助其他人掌握编程概念。

列出所有Java教程