实现 Closeable 或实现 AutoCloseable

Posted

技术标签:

【中文标题】实现 Closeable 或实现 AutoCloseable【英文标题】:implements Closeable or implements AutoCloseable 【发布时间】:2012-10-19 22:17:28 【问题描述】:

我正在学习 Java,在implements Closeableimplements AutoCloseable 接口上找不到任何好的解释。

当我实现 interface Closeable 时,我的 Eclipse IDE 创建了一个方法 public void close() throws IOException

我可以在没有接口的情况下使用pw.close(); 关闭流。但是,我无法理解如何使用接口实现close() 方法。而且,这个接口的用途是什么?

我也想知道:如何检查iostream 是否真的关闭了?

我使用的是下面的基本代码

import java.io.*;

public class IOtest implements AutoCloseable 

public static void main(String[] args) throws IOException  

    File file = new File("C:\\test.txt");
    PrintWriter pw = new PrintWriter(file);

    System.out.println("file has been created");

    pw.println("file has been created");



@Override
public void close() throws IOException 



【问题讨论】:

我想所有的都已经说了,但也许你对下面关于尝试资源的文章感兴趣:docs.oracle.com/javase/tutorial/essential/exceptions/…。这也可能有助于理解给定的答案。 【参考方案1】:

AutoCloseable(在 Java 7 中引入)使得使用 try-with-resources 成语成为可能:

public class MyResource implements AutoCloseable 

    public void close() throws Exception 
        System.out.println("Closing!");
    


现在你可以说:

try (MyResource res = new MyResource()) 
    // use resource here

JVM 会自动为你调用close()

Closeable 是一个较旧的接口。 出于某种原因 为了保持向后兼容性,语言设计者决定单独创建一个。这不仅允许在 try-with-resources 中使用所有 Closeable 类(如抛出 IOException 的流),而且还允许从 close() 抛出更一般的检查异常。

如有疑问,请使用AutoCloseable,您班级的用户将不胜感激。

【讨论】:

原因很简单:Closeable.close() 抛出IOException。许多可能受益于 try-with-resources 的 close() 方法会抛出其他检查异常(例如 java.sql.Connection.close() 所以 AutoCloseable.close() 会抛出 Exception。更改现有的 Closeable 合同将破坏所有现有的应用程序/库依赖close() 仅抛出 IOException 而不是所有(已检查)异常的合同。 @MarkRotteveel:+1,谢谢。我更正了我的答案以反映您的建议和 cmets。 另外:Closeable.close() 必须是幂等的。 AutoCloseable.close() 不是,但仍强烈推荐。 另外,不要使用默认的public void close( ) throws Exception——如果可以的话,使用更具体的异常(例如 IOException) Closeable保证幂等性。它要求在用户实现close() 方法时具有幂等性。而IOException 是否更具体/更合适取决于用例。【参考方案2】:

Closeable extends AutoCloseable,专门用于 IO 流:它抛出 IOException 而不是 Exception,并且是幂等的,而 AutoCloseable 不提供此保证。

这在两个接口的javadoc中都有解释。

实现AutoCloseable(或Closeable)允许将类用作Java 7中引入的try-with-resources构造的资源,它允许在块的末尾自动关闭此类资源,而无需添加finally 块显式关闭资源。

您的类不代表可关闭的资源,实现此接口绝对没有意义:IOTest 无法关闭。甚至不可能实例化它,因为它没有任何实例方法。请记住,实现接口意味着类和接口之间存在 is-a 关系。你这里没有这种关系。

【讨论】:

只需为流相关的类实现 Closable,为其他需要自动关闭功能的类实现 AutoClosable【参考方案3】:

在我看来,您对接口不是很熟悉。在您发布的代码中,您无需实现AutoCloseable

如果您即将实现自己的PrintWriter,您只需(或应该)实现CloseableAutoCloseable,它处理文件或任何其他需要关闭的资源。

在您的实现中,调用pw.close() 就足够了。您应该在 finally 块中执行此操作:

PrintWriter pw = null;
try 
   File file = new File("C:\\test.txt");
   pw = new PrintWriter(file);
 catch (IOException e) 
   System.out.println("bad things happen");
 finally 
   if (pw != null) 
      try 
         pw.close();
       catch (IOException e) 
      
   

上面的代码与 Java 6 相关。在 Java 7 中,这可以更优雅地完成(参见 this answer)。

【讨论】:

为什么只使用PrintWriter?特别是 AutoClosable 对象可以在更多情况下使用,而不仅仅是 PrintWriters... 你是绝对正确的。问题是关于PrintWriter,所以我提到它更具体。 为什么要在AutoCloseable 的上下文中描述Java 6 的情况?最好显示 try-with-resources 来代替……【参考方案4】:

这是一个小例子

public class TryWithResource 

    public static void main(String[] args) 
        try (TestMe r = new TestMe()) 
            r.generalTest();
         catch(Exception e) 
            System.out.println("From Exception Block");
         finally 
            System.out.println("From Final Block");
        
    




public class TestMe implements AutoCloseable 

    @Override
    public void close() throws Exception 
        System.out.println(" From Close -  AutoCloseable  ");
    

    public void generalTest() 
        System.out.println(" GeneralTest ");
    

这是输出:

GeneralTest 
From Close -  AutoCloseable  
From Final Block

【讨论】:

最好也写输出,这样短代码就不需要试用项目了。 在close()方法中,我们不需要显式关闭资源吗?也许只有打印语句。 @ShaileshWaghmare 是的。但出于测试目的,我在代码片段中提到了。 @LovaChittumuri 那么它会像this.close() 还是代码中的东西?因为它是自动调用的。(只是为了确定) @shailesh Waghmare 你想测试我吗?【参考方案5】:

最近我读了一本 Java SE 8 Programmer Guide ii Book。

我发现了 AutoCloseableCloseable 之间的区别。

AutoCloseable 接口是在 Java 7 中引入的。在此之前,另一个接口 存在称为Closeable。它类似于语言设计者想要的,具有 以下例外:

Closeable 将抛出的异常类型限制为IOExceptionCloseable 要求实现是幂等的。

语言设计者强调向后兼容性。由于改变现有 界面不受欢迎,他们制作了一个名为AutoCloseable 的新界面。这个新 接口没有Closeable那么严格。由于Closeable 符合要求 AutoCloseable,它是在引入 AutoCloseable 时开始实现的。

【讨论】:

与其说“这个新接口没有Closeable那么严格”,我建议说“这个新接口可以在更一般的上下文中使用,在关闭期间抛出的异常不是必然是一个 IOException”。在 Java 世界中,“不那么严格”有负面影响。【参考方案6】:

try-with-resources 声明。

try-with-resources statement 是声明一个或多个资源的try 语句。 resource 是一个在程序完成后必须关闭的对象。 try-with-resources statement 确保每个资源在语句结束时关闭。任何实现java.lang.AutoCloseable 的对象,包括所有实现java.io.Closeable 的对象,都可以用作资源。

以下示例从文件中读取第一行。它使用BufferedReader 的实例从文件中读取数据。 BufferedReader 是一个资源,程序结束后必须关闭它:

static String readFirstLineFromFile(String path) throws IOException 
    try (BufferedReader br =
                   new BufferedReader(new FileReader(path))) 
        return br.readLine();
    

在本例中,try-with-resources 语句中声明的资源是 BufferedReader。声明语句紧跟在 try 关键字之后的括号内。在 Java SE 7 及更高版本中,类 BufferedReader 实现了接口 java.lang.AutoCloseable。因为BufferedReader 实例是在try-with-resource 语句中声明的,所以无论try 语句是正常完成还是突然完成(作为BufferedReader.readLine 方法抛出IOException 的结果),它都会被关闭。

在 Java SE 7 之前,您可以使用 finally 块来确保关闭资源,无论 try 语句是正常完成还是突然完成。以下示例使用 finally 块而不是 try-with-resources 语句:

static String readFirstLineFromFileWithFinallyBlock(String path)
                                                     throws IOException 
    BufferedReader br = new BufferedReader(new FileReader(path));
    try 
        return br.readLine();
     finally 
        if (br != null) br.close();
    


请refer to the docs。

【讨论】:

以上是关于实现 Closeable 或实现 AutoCloseable的主要内容,如果未能解决你的问题,请参考以下文章

是否将 Closeable 的新实例分配给先前创建的实例,关闭先前的实例?

接口 Closeable

JavaWriter源码分析

JavaWriter源码分析

输入输出流如何 close 不会出现异常

MediaPlayer 在暂停或 seekTo 后不会继续显示字幕 (.SRT)