如何在Java中通过AutoCloseable追踪实例是否已关闭?求代码示例
《Effective Java》第8条建议的实现疑问
我正在阅读Joshua Bloch所著的《Effective Java》。在第二章的第8条建议:避免使用Finalizers和Cleaners中,作者指出了使用终结器和清理器进行内存管理的问题。有文章对终结器的概念做了很好的理论解释,也有回答充分展示了finalize()方法的问题,尤其是在安全性方面。
书中推荐使用AutoCloseable作为替代方案:
那么,对于封装了需要终止的资源(如文件或线程)的类,我们应该用什么来替代终结器或清理器呢?只需让你的类实现
AutoCloseable接口,并要求客户端在实例不再使用时调用close方法,通常使用try-with-resources确保即使发生异常也能终止资源(第9条建议)。
不过作者补充了一个要点:必须追踪实例是否已关闭,他提到:
有一个值得注意的细节:实例必须追踪自身是否已关闭:
close方法需要在字段中记录对象已失效,其他方法需检查该字段,若在实例关闭后被调用则抛出IllegalStateException。
遗憾的是,书中并未给出编写API时的具体代码示例。我此前的相关研究也未找到符合该要求的实现,希望能获取对应的代码示例。
我找到的相关高赞问题中有一个回答提出了一种让客户端无需承担此责任的替代方案,但这并未解答我关于Joshua Bloch所述内容的疑问。
非常感谢!
符合要求的代码示例
下面是遵循上述建议的资源类实现,以封装文件资源为例:
import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; public class ResourceHandler implements AutoCloseable { // 标记资源是否已关闭 private boolean closed = false; private BufferedReader reader; public ResourceHandler(String filePath) throws IOException { this.reader = new BufferedReader(new FileReader(filePath)); } // 核心业务方法:读取一行内容 public String readLine() throws IOException { checkIfClosed(); return reader.readLine(); } // 检查资源状态,已关闭则抛出异常 private void checkIfClosed() { if (closed) { throw new IllegalStateException("Resource has been closed"); } } @Override public void close() throws IOException { // 确保close方法幂等,多次调用不会出错 if (!closed) { reader.close(); closed = true; } } // 可选:添加终结器作为安全网(仅作为最后兜底,不依赖它) @Override protected void finalize() throws Throwable { try { close(); } finally { super.finalize(); } } }
使用示例(try-with-resources)
public class Main { public static void main(String[] args) { try (ResourceHandler handler = new ResourceHandler("test.txt")) { String line; while ((line = handler.readLine()) != null) { System.out.println(line); } } catch (IOException e) { e.printStackTrace(); } } }
关键细节说明
- 状态追踪:用
closed布尔字段记录资源状态,所有业务方法(如readLine)调用前都会先执行checkIfClosed()检查,确保资源未关闭才能继续操作。 - 幂等的close方法:
close方法中先判断closed状态,避免重复关闭资源导致的异常。 - try-with-resources支持:因为实现了
AutoCloseable,客户端可以使用try-with-resources语法,无需手动调用close,JVM会自动处理资源释放。 - 可选安全网:添加
finalize()作为最后兜底,但正如书中所说,不能依赖它,只是防止客户端忘记关闭资源时的最后保障。
内容的提问来源于stack exchange,提问作者Rahul
相关产品推荐
相关产品推荐

