行为差异:'null'初始化最终静态成员,'null'初始化最终局部变量

我在后续代码中遇到了一个我之前不知道的行为。

考虑第一情况:

public static void main(String[] args) {    
    final String str = null;
    System.out.println(str.length());  // Compiler Warning: NullPointerAccess
}

正如预期的那样,编译器在为 null 时向我显示以下警告 - Null 指针访问:变量 str 在此位置只能为 null。str

现在,当我移动该变量时,静态最终字段初始化为 null

class Demo {
    static final String str = null;

    public static void main(String[] args) {
        System.out.println(str.length());  // No Compiler Warning
    }
}

现在,编译器不显示任何警告。AFAIK,编译器应该知道,作为最终的,不会改变其值,在代码的任何时候。鉴于它是 ,肯定会在以后导致,它确实如此。strnullNullPointerException

虽然编译器在第一种情况下成功地警告了我这一点,但为什么它在第二种情况下无法识别这一点。为什么会发生这种行为改变?如果我将字段更改为字段,并使用 的实例访问它,则行为是相同的。staticinstanceDemo

我认为此行为可能已在 JLS 中指定,因此我浏览了主题“确定分配”,但没有找到与此问题相关的任何内容。谁能解释一下行为的变化?如果可能的话,我正在寻找一些与JLS链接的强项?

除此之外,为什么编译器首先只向我显示警告,因为我认为出于我上面提到的相同原因,方法调用肯定会在运行时抛出NPE,因为字段无法更改?为什么它没有向我显示编译器错误?我是否对编译器期望过高,因为似乎很明显,运行时结果不能是任何东西?str.length()NPE


很抱歉之前错过了:

我正在使用Eclipse Juno,在Ubuntu 12.04OpenJDK 7上。


答案 1

我不确定100%,但是在第二种情况下,如果您有针对局部变量的最终字段,则可能会在静态(或实例块取决于变量是否为静态)初始化块中直接分配给此最终字段一些值:

class Demo {
...
static {
 str = "some text";
}
...
}

所以编译器不会给你警告。


答案 2

哇!事实证明,这是日食特有的问题。当我使用以下命令编译代码时:

javac -Xlint:all Demo.java

它没有显示任何情况的任何警告。因此,我回到eclipse来检查为这种情况启用的任何设置,并找到了一个。

Windows -> 首选项 -> Java -> 编译器 -> 错误/警告中,在 Null Analysis 下,我可以更改 Eclipse Compiler 应处理 Null 指针访问的方式。我可以将其设置为 - 忽略错误警告

现在这似乎是一个完全愚蠢的问题。我感到羞耻。:(


推荐