try-with-resources细节

4

在使用对象时,我们有三个基本步骤:

  1. 声明
  2. 实例化
  3. 初始化

我的问题是,在try-with语句的()部分中必须执行哪些步骤才能自动关闭资源。

示例1 - 在此代码中,FileReader对象是否会自动关闭:

try (BufferedReader br = new BufferedReader(new FileReader(filePath)))
{
//some code;
} 

例2 - 在此代码中,buf2是否会自动关闭:
private static BufferedReader buf1;

public static void main(String[] args) throws IOException {
    //some code
    try (BufferedReader buf2 = buf1)
    {

    } 
 }

附言:有人认为这个问题是Try With Resources vs Try-Catch的重复。但实际上不是,那个问题是关于try-catch和try-with-resources之间的区别,而我的问题是关于try-with的细节。


3
听起来像是一道作业题,但这是个提示:使用“try-with-resources”语句块时,必须声明的变量实现了AutoCloseable接口,并将在隐式finally块中调用close()方法。 - Andreas
1
准备(学习)认证考试就像做家庭作业一样。谷歌搜索OCPJP甚至会链接到“Oracle大学”网站。也许Java教程中的第一段关于“try-with-resources语句”的内容可以帮助你。 - Andreas
1
如果你真的想认真学习Java,就不应该害怕尝试。创建一个AutoCloseable类,在close()方法中添加一个System.out.println语句。使用此类的上述代码替换BufferedReader。看看是否会打印出什么。或者只需在BufferedReader.close()中添加调试器断点并查看是否已到达。 - JB Nizet
@JB Nizet,感谢您的时间。我并不害怕。问题在于,我更喜欢理解原则而不是任何情况。我在这里写下这些话,是因为我希望有人能够给出自动关闭案例的主要原则。 - Pavel_K
2个回答

7
每当需要语言相关的细节时,最完整的参考资料是Java语言规范(只需Google一下即可)。对于try-with-resources语句,您可以阅读第14.20.3节,其中指出以下内容:
try ({VariableModifier} R Identifier = Expression ...)
    Block

被翻译成

{
   final {VariableModifierNoFinal} R Identifier = Expression; 
   Throwable #primaryExc = null;
   try ResourceSpecification_tail
      Block catch (Throwable #t) {
         #primaryExc = #t;
         throw #t;
      } finally {
         if (Identifier != null) {
            if (#primaryExc != null) { 
               try { 
                  Identifier.close(); 
               } catch (Throwable #suppressedExc) { 
                  #primaryExc.addSuppressed(#suppressedExc);
               }
            } else {
               Identifier.close();
            }
         }
     }
}

在您的第一个示例中,资源RBufferedReader标识符br表达式new BufferedReader(new FileReader(filePath))。因此,只有BufferedReader在隐式的finally块中关闭。finally块不会调用close方法关闭FileReader,因为它不是资源声明本身的一部分。然而BufferedReader.close()的实现内部调用了包装的FileReaderclose方法。所以第一个问题的答案是肯定的,仅仅是因为包装对象关闭了它(遵循一个资源在被释放时应该释放任何包装资源的共同智慧),并不是因为try-with-resources。

在第二个示例中:

private static BufferedReader buf1;

public static void main(String[] args) throws IOException {
    //some code
    try (BufferedReader buf2 = buf1)
    {

    } 
}

答案取决于一些代码。这里buf2buf1都指向内存中的同一个对象。如果这个"一些代码"将buf1初始化为某个对象,则由于buf2也引用它,所以该对象将被关闭。如果没有并且buf1为null(因此buf2也为null),则由于上面隐式finally中的null检查,不会关闭任何内容。

1
  1. FileReader will be closed. But that is not because it is in the try statement. It is because when BufferedReader is closed, it calls close() on FileReader as well. For a counter example, I have a class called X that implements AutoCloseable. And that class needs a Foo object in its constructor. So I write:

    try (X x = new X(new Foo())) {
    
    }
    

Foo会被关闭吗?它甚至没有实现AutoCloseable接口!

  1. I wrote the following to test this:

    BufferedReader buf1 = null;
    try (BufferedReader buf2 = buf1) {
    
    }
    

它完美地工作了,没有异常!我的猜测是,在 try 语句的结尾处,它会检查对象是否为 null。如果不是,则关闭它。因此,在这种情况下,因为 buf2 是 null,它无法被关闭。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接