静态块java中的常量问题



下面的代码中,我有两个关于静态块和常量的问题。

  1. 常量(甚至是简单的静态变量)不能直接从静态块引用。它给出的错误是"在定义字段之前不能引用字段"。但是,当通过静态方法进行访问时,这是可以的
  2. 如果我在静态块的catch中为一个常量赋值,如下所述,它会给出一个错误,说"最终字段NAME可能已经被赋值了"。但如果在catch中执行asigning,则会出现错误,称"空白的最终字段NAME可能尚未初始化"

我想知道为什么会这样?

代码:

public class TestStaticblock {
    static{
        try {
//          NAME = dummyStringValue() + NAME_APPENDER; // Cannot reference a field before it is defined
//          NAME = dummyStringValue() + getNameAppender(); // This is OK
            NAME = dummyStringValue();
        } catch (Exception e) {
            NAME = null; // The final field NAME may already have been assigned
        }
    }
    private static String dummyStringValue() throws Exception{
        return "dummy";
    }
    private static String getNameAppender() throws Exception{
        return NAME_APPENDER;
    }
    private static final String NAME; // If I comment Catch it says "The blank final field NAME may not have been initialized"
    private static  String NAME_APPENDER = "appender";
}

只能分配给NAME一次(因为它是final)。将结果分配给一个临时变量,然后分配给NAME(不要默默地吞下Exception s)。类似

static {
    String temp = null;
    try {
        temp = dummyStringValue();
    } catch (Exception e) {
        e.printStackTrace();
    }
    NAME = temp;
}

不能以当前方式分配NAME的原因是编译器执行静态程序分析(特别是数据流分析),并检测到可能存在未分配NAME的代码路径。因为NAME是final,所以这是一个编译错误。

在分配static final字段之前,不能在静态块中使用它,但您可以通过调用方法来访问它。

例如,此代码打印null FOO:

public class Main {
    static final String FOO;
    static {
        foo();
        FOO = "FOOFOO".substring(0, 3);
        foo();
    }
    static void foo() {
        System.out.println(FOO);
    }
    public static void main(String[] args) {}
}

不可否认,这很奇怪,但我想,如果让这样的事情变得不可能,语言会变得更加复杂。

至于你的第二个问题,这并不能编译。

static{
    try {
        NAME = dummyStringValue();
    } catch (Exception e) {
        NAME = null; // The final field NAME may already have been assigned
    }
}

这也很奇怪。如果抛出异常,则它只能发生在方法dummyStringValue()内部。由于不能在方法内为final字段赋值,因此完全不可能在catch块中已经为NAME变量赋值。因此,不存在不分配NAME的可能的代码路径。你会认为它应该以与相同的方式工作

static{
    if (someCondition()) {
        NAME = dummyStringValue();
    } else {
        NAME = null;
    }
}

它编译得很好。

我想原因再次是,如果允许这样做,语言会变得更加复杂。允许它没有太大的好处,因为你可以只使用一个方法或temp变量,如其他答案所示。异常只是比if语句更复杂——它们的行为几乎就像goto。@ElliotRisch在评论中提出了一个很好的观点。像这样的东西怎么样:

static{
    try {
        NAME1 = dummyStringValue1();
        NAME2 = dummyStringValue2();
    } catch (Exception e) {
        // Has NAME1 been assigned here?
    }
}

也许这对那些寻找类似东西的人会有所帮助。

Java有一个鲜为人知的功能(在JavaSpecialists从字段抛出异常中讨论过),如果您希望将final实例变量(即NOTstatic)初始化为抛出异常的方法调用的结果,则可以通过添加抛出异常的构造函数来避免明显的错误。请注意,此解决方案仅适用于非静态(而不是您所观察到的)。

public class TestStaticblock {
    private final String NAME = dummyStringValue();
    // Adding this removes the "unreported Exception" above.
    public TestStaticblock() throws Exception {
    }
    private static String dummyStringValue() throws Exception {
        return "dummy";
    }
}

我个人最喜欢使用方法,而不是初始化单个变量的静态初始化器块:

private static final String NAME = getName();
private static String getName() {
  try {
    return something();
  } catch (Exception e) {
    return null;
  }
}
  1. 你不会遇到像你所描述的那样的问题
  2. 您只能计算一个字段值,所以您不想在同一块中放入很多东西
  3. 您可以重新调用一个方法来测试它

最新更新