了解 Java 9 中的 try-with-resources 增强

Posted

技术标签:

【中文标题】了解 Java 9 中的 try-with-resources 增强【英文标题】:Understanding try-with-resources enhancement in Java 9 【发布时间】:2020-11-04 21:32:06 【问题描述】:

不要像这样在尝试使用资源块中初始化资源

try(FileWriter fw = new FileWriter ("java.txt")) 
    //some operation
catch (IOException ioe) 
    ioe.printStackTrace ();

,我们现在可以传递一个对try with resources block的引用,我们希望自动关闭,如下所示:

FileWriter fw = new FileWriter ("java.txt");

try(fw) 
    //some operation
catch (IOException ioe)
    ioe.printStackTrace ();

到目前为止,这部分很清楚。我的问题是为什么我将在下一个示例中显示的代码不起作用?它与前面编译得很好的例子有什么区别?

public class Test 

    private FileWriter fileWriter;

    public Test (FileWriter fileWriter) throws IOException 
        this.fileWriter = fileWriter;
    

    
        try(fileWriter)  //compile error:
//"Variable used as a try-with-resources resource should be final or effectively final""
        catch (IOException ioe)
            ioe.printStackTrace ();
        
    


有人可以帮助我理解这条消息的含义吗?我不知道这里可能是什么问题。我还尝试将 final 放入 fileWriter 变量声明中,但没有解决问题。

【问题讨论】:

【参考方案1】:

我收到以下编译器错误,与您的不匹配:

The blank final field fileWriter may not have been initialized

这是因为您在实例初始化程序中引用fileWriter,它在超类构造函数调用和显式构造函数调用之后但在构造函数中的任何剩余代码之前执行。根据JLS, Section 12.5, "Creation of New Class Instances":

    将构造函数的参数分配给此构造函数调用的新创建的参数变量。

    如果此构造函数以对同一类中的另一个构造函数(使用 this)的显式构造函数调用(第 8.8.7.1 节)开始,则 评估构造函数调用的参数和过程 递归地使用这五个相同的步骤。如果那个构造函数 调用突然完成,然后这个过程突然完成 出于同样的原因;否则,继续第 5 步。

    此构造函数不是以显式构造函数调用同一类中的另一个构造函数开始的(使用 this)。如果 this 构造函数用于 Object 以外的类,然后 this 构造函数将以显式或隐式调用 超类构造函数(使用 super)。评估论据和 使用这些递归处理超类构造函数调用 同样的五个步骤。如果该构造函数调用突然完成, 然后这个过程出于同样的原因突然完成。除此以外, 继续第 4 步。

    执行该类的实例初始化器和实例变量初始化器,分配实例变量的值 对应实例变量的初始化器,在 它们在源代码中以文本形式出现的从左到右的顺序 为班级。如果执行这些初始化程序中的任何一个导致 异常,则不再处理任何初始化程序,这 过程突然完成,但出现同样的异常。除此以外, 继续第 5 步。

    执行此构造函数的其余部分。如果该执行突然完成,则此过程会突然完成 同样的原因。否则,此过程正常完成。

粗体强调我的)

这意味着你的构造函数还没有给fileWriter分配任何东西,导致了实际的错误。

要么在fileWriter初始化之后将try-with-resources块放在构造函数中,要么将其移到方法中。

【讨论】:

【参考方案2】:

这实质上意味着 Java 希望您确保传递给 try-with-resources 的变量(在本例中为 fileWriter)在分配后不会被更改。如果可以更改变量(例如在 try 块本身内),则可能会出现各种未定义的行为。

从代码的角度来看,要使其工作,请在 fileWriter 的声明中添加 final

public class Test 

    private final FileWriter fileWriter;  // add 'final'

    public Test (FileWriter fileWriter) throws IOException 
        this.fileWriter = fileWriter;
    
 
    // ... in some other method
    private void foo() 
        try(fileWriter) 
         catch (IOException ioe)
            ioe.printStackTrace ();
        
    


【讨论】:

那么当我在第二个代码示例中传递fileWriter 变量时,为什么Java 没有抱怨呢?注意我没成功final @Markus 因为局部变量可以实际上是最终的 哦,对了,我完全忘记了。如果我可以问,还有一件事。 Aziz 的示例是如何工作的,但同样的代码只是放在初始化程序块中没有?他们不一样吗?来源:pastebin.pl/view/6cde5260 @Markus 是 same 编译器错误吗?我很确定诸如内联字段初始化和初始化程序块之类的东西基本上由编译器折叠到构造函数中,并且在构造函数中编写的显式代码之前执行。因此,在您的代码中,就好像您在 fileWriter = ... 之前编写了 try-catch 块。 哦,对了,你对那个顺序是完全正确的。最后,让一个添加更多的东西。 Effective finally variable is when it is initialized only once. 在这种情况下(正如我在问题中的代码所示)它最终不应该有效吗?我只在构造函数上初始化它一次,但我得到编译错误,它应该是最终的或有效的最终。为什么?代码在这里,所以你不必滚动:pastebin.pl/view/154ae177

以上是关于了解 Java 9 中的 try-with-resources 增强的主要内容,如果未能解决你的问题,请参考以下文章

Java 9 - 发布者和订阅者如何工作

卷不动了?300秒快速了解这些年Java 9 - 16的新特性,助你脱离内卷

300 秒快速了解 Java 9 - 16 新特性

9冯诺依曼体系结构了解吗

9冯诺依曼体系结构了解吗

300 秒快速了解 Java 9 - 16 新特性