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

时间:2013-08-02 13:13:18

标签: java compiler-warnings final static-members local-variables

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

考虑1 st 案例:

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

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

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

class Demo {
    static final String str = null;

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

现在,编译器没有显示任何警告。 AFAIK,编译器应该知道str是最终的,不会在代码的任何一点改变它的值。鉴于它是null,肯定会在NullPointerException之后产生,而它会发生。{/ p>

虽然编译器在第一种情况下成功警告我,为什么它在第二种情况下无法识别。为什么这种行为改变?如果我将static字段更改为instance字段,并使用Demo的实例访问该字段,则行为相同。

我认为此行为可能已在JLS中指定,因此我浏览了主题 Definite Assignment ,但没有找到与此问题相关的任何内容。任何人都可以解释行为的变化吗? 如果可能的话,我正在寻找一些与JLS链接的强点吗?

除此之外,为什么编译器首先只显示警告,因为我认为出于与上述相同的原因,方法调用肯定会抛出 NPE 在运行时,由于该字段无法更改?为什么不向我显示编译器错误?我是否期望编译器太多,因为似乎很明显,str.length()的运行时结果不能超过NPE


很抱歉错过了之前:

我在 Ubuntu 12.04 上使用 Eclipse Juno OpenJDK 7

3 个答案:

答案 0 :(得分:2)

我不确定100%,但是在第二种情况下,你有最终字段对抗局部变量,有可能在这个最终字段中指定一些直接在静态的值(或实例块取决于变量是否为静态)初始化块:

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

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

答案 1 :(得分:2)

哇!原来,这是日食的具体问题。当我使用:

编译代码时
javac -Xlint:all Demo.java

它没有对任何案件发出任何警告。所以,我回到eclipse检查为这种情况启用的任何设置,并找到了一个。

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

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

答案 2 :(得分:1)

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

在这种情况下,str是一个局部变量,如果在初始化之前尝试对其执行任何操作,编译器将无法编译。流分析完全不同,它将检查代码流,它检测到在执行length()操作时,局部变量str只能为空。

class Demo {
    static final String str = null;

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

在这种情况下,str是一个实例变量,即使您没有明确指定,它也将为null。

为什么这里没有警告?

您可以在构造函数中初始化实例变量。或者你可以在调用lenght()操作之前调用setter方法。所以它逃脱了流分析(编译器不确定实例变量在该点是否为空,但在第一种情况下编译确保局部变量将始终为null)。