ZetCode

Java Closeable 接口

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

java.io.Closeable 接口表示一个持有需要显式释放的资源的对象。它定义了一个单独的 close 方法来释放这些资源。实现此接口的类通常表示 I/O 流、通道或其他资源密集型对象。

Closeable 在 Java 5 中引入,并且是 java.io 包的一部分。 自 Java 7 以来,它扩展了 AutoCloseable,从而能够与 try-with-resources 语句一起使用。 正确的资源管理对于防止 Java 应用程序中的资源泄漏至关重要。

Closeable 接口概述

Closeable 接口很简单,但对于资源管理至关重要。 其主要目的是确保正确清理文件句柄、网络连接或内存缓冲区等资源。 该接口仅声明一个必须实现的方法。

public interface Closeable extends AutoCloseable {
    void close() throws IOException;
}

上面的代码显示了完整的 Closeable 接口定义。 如果资源无法正确释放,则 close 方法可能会抛出 IOException。 实现应该是幂等的,允许多次调用而没有副作用。

基本 Closeable 实现

此示例演示了 Closeable 接口的一个简单的自定义实现。 该类管理一个虚构的资源,该资源需要正确清理。 该实现显示了资源管理的基本模式。

Main.java
import java.io.Closeable;
import java.io.IOException;

public class ResourceHolder implements Closeable {
    private boolean closed = false;
    
    public ResourceHolder() {
        System.out.println("Resource acquired");
    }
    
    public void doWork() {
        if (closed) {
            throw new IllegalStateException("Resource is closed");
        }
        System.out.println("Performing work with resource");
    }
    
    @Override
    public void close() throws IOException {
        if (!closed) {
            System.out.println("Releasing resource");
            closed = true;
        }
    }
    
    public static void main(String[] args) {
        try (ResourceHolder holder = new ResourceHolder()) {
            holder.doWork();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例显示了一个基本的 Closeable 实现。 ResourceHolder 类跟踪其关闭状态,并在关闭后阻止使用。 try-with-resources 语句确保自动调用 close,即使在工作期间发生异常也是如此。

使用 Closeable 的文件处理

文件流是 Java 中常见的 Closeable 实现。 此示例演示了使用实现 CloseableFileInputStream 进行正确的文件处理。 正确的资源清理对于文件操作至关重要。

Main.java
import java.io.FileInputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("example.txt");
            int content;
            while ((content = fis.read()) != -1) {
                System.out.print((char) content);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

此示例显示了传统的带有显式 close 的文件处理,位于 finally 块中。 FileInputStream 实现了 Closeable,需要正确清理。 嵌套的 try-catch 确保关闭操作本身不会导致资源泄漏。

Try-With-Resources 示例

Java 7 引入了 try-with-resources,简化了 Closeable 资源管理。 此示例演示了处理 Closeable 资源的现代方法。 当块退出时,该语法会自动调用 close

Main.java
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("example.txt"))) {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例将 try-with-resources 与实现 CloseableBufferedReader 一起使用。 该资源在 try 之后的括号中声明,并且会自动关闭。 这种方法比手动 finally 块更简洁且不易出错。

多个 Closeable 资源

Try-with-resources 可以同时管理多个 Closeable 资源。 资源按照声明的相反顺序关闭。 此示例演示了在单个块中正确处理多个资源。

Main.java
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("source.txt");
             FileOutputStream fos = new FileOutputStream("destination.txt")) {
            
            byte[] buffer = new byte[1024];
            int length;
            while ((length = fis.read(buffer)) > 0) {
                fos.write(buffer, 0, length);
            }
            System.out.println("File copied successfully");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例使用 FileInputStreamFileOutputStream 复制文件,它们都是 Closeable 实现。 资源在 try-with-resources 声明中用分号分隔。 它们将以相反的顺序自动关闭(首先是 fos,然后是 fis)。

具有异常处理的自定义 Closeable

此示例演示了一个更复杂的 Closeable 实现,带有正确的异常处理。 该类管理一个需要正确清理的数据库连接。 异常处理对于资源管理至关重要。

Main.java
import java.io.Closeable;
import java.io.IOException;

public class DatabaseConnection implements Closeable {
    private boolean connected = false;
    
    public DatabaseConnection(String url) throws IOException {
        connect(url);
    }
    
    private void connect(String url) throws IOException {
        // Simulate connection
        if (url == null || url.isEmpty()) {
            throw new IOException("Invalid connection URL");
        }
        connected = true;
        System.out.println("Connected to database: " + url);
    }
    
    public void query(String sql) throws IOException {
        if (!connected) {
            throw new IOException("Not connected to database");
        }
        System.out.println("Executing query: " + sql);
    }
    
    @Override
    public void close() throws IOException {
        if (connected) {
            System.out.println("Closing database connection");
            connected = false;
        }
    }
    
    public static void main(String[] args) {
        try (DatabaseConnection db = new DatabaseConnection("jdbc:example:db")) {
            db.query("SELECT * FROM users");
        } catch (IOException e) {
            System.err.println("Database error: " + e.getMessage());
        }
    }
}

此示例显示了一个实现 CloseableDatabaseConnection 类。 构造函数和方法会按照接口的要求抛出 IOException。 try-with-resources 确保连接正确关闭,即使在查询执行期间发生异常也是如此。

Closeable 与 AutoCloseable

此示例重点介绍了 CloseableAutoCloseable 之间的区别。 虽然相似,但它们具有不同的用途和异常规范。 了解这些差异对于正确的接口实现非常重要。

Main.java
import java.io.Closeable;
import java.io.IOException;
import java.lang.AutoCloseable;

public class Main {
    static class CloseableResource implements Closeable {
        @Override
        public void close() throws IOException {
            System.out.println("CloseableResource.close()");
            throw new IOException("Closeable exception");
        }
    }
    
    static class AutoCloseableResource implements AutoCloseable {
        @Override
        public void close() throws Exception {
            System.out.println("AutoCloseableResource.close()");
            throw new Exception("AutoCloseable exception");
        }
    }
    
    public static void main(String[] args) {
        try (CloseableResource cr = new CloseableResource();
             AutoCloseableResource acr = new AutoCloseableResource()) {
            System.out.println("Using resources");
        } catch (Exception e) {
            System.out.println("Caught exception: " + e.getMessage());
            for (Throwable t : e.getSuppressed()) {
                System.out.println("Suppressed: " + t.getMessage());
            }
        }
    }
}

此示例演示了接口之间的主要区别。 Closeable.close 仅抛出 IOException,而 AutoCloseable.close 可以抛出任何 Exception。 当在 try-with-resources 中一起使用时,异常会被正确处理,并且可以访问抑制的异常。

来源

Java Closeable 接口文档

在本文中,我们介绍了 Java Closeable 接口的基本方面。 了解资源管理对于编写能够正确处理系统资源的健壮的 Java 应用程序至关重要。

作者

我叫 Jan Bodnar,是一位敬业的程序员,在该领域拥有多年的经验。 我于 2007 年开始撰写编程文章,此后撰写了超过 1,400 篇文章和八本电子书。 凭借超过八年的教学经验,我致力于分享我的知识并帮助其他人掌握编程概念。

列出所有Java教程