了解Java 9中的尝试资源增强功能

问题描述

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

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

,我们现在只需传递对尝试使用资源块的引用,就可以自动关闭,如下所示:

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变量声明中,但这并不能解决问题。

解决方法

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

The blank final field fileWriter may not have been initialized

这是因为您要在实例初始化程序中引用fileWriter,该初始化程序在超类构造函数调用和显式构造函数调用之后但在构造函数中的所有其余代码之前执行。根据{{​​3}}:

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

  2. 如果此构造函数以同一个类中的另一个构造函数的显式构造函数调用(第8.8.7.1节)开头(使用此方法),则 评估参数并处理构造函数调用 递归地使用这五个步骤。如果那个构造函数 调用突然完成,然后此过程突然完成 出于同样的原因;否则,请继续执行步骤5。

  3. 此构造函数并不以相同类中的另一个构造函数的显式构造函数调用开头(使用此函数)。如果 该构造函数用于Object以外的其他类,则此 构造函数将以显式或隐式调用 超类构造函数(使用super)。评估论点并 使用这些递归处理超类构造函数调用 同样的五个步骤。如果该构造函数调用突然完成, 然后由于相同的原因,此过程突然完成。除此以外, 继续执行步骤4。

  4. 执行此类的实例初始化器和实例变量初始化器,分配实例变量的值 初始化为对应的实例变量,在 从左到右的顺序,它们在源代码中以文本形式显示 上课如果执行这些初始化程序中的任何一个都会导致 异常,则不会再处理其他初始化程序,并且这 程序突然完成,但有相同的例外。除此以外, 继续执行步骤5。

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

粗体强调我的)

这意味着您的构造函数尚未将任何内容分配给fileWriter,从而导致实际错误。

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

,

从本质上讲,这意味着Java希望您确保传递给try-with-resources的变量(在这种情况下为fileWriter)一旦分配就不会更改。如果可以更改变量(例如在try块本身内),则可能会出现各种不确定的行为。

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

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 ();
        }
    }

}