我在后续代码中遇到了一个我以前不知道的行为。
考虑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 。
答案 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)。