[英]Behaviour Difference: 'null' initialized final static member, and 'null' initialized final local variable
在后续代码中,我遇到了一个我之前不知道的行为。
考虑第一种情况:
public static void main(String[] args) {
final String str = null;
System.out.println(str.length()); // Compiler Warning: NullPointerAccess
}
正如预期的那样,编译器在str
为null时显示以下警告 - 空指针访问:变量str在此位置只能为null。
现在,当我将该变量移动一个初始化为null的静态final字段时 :
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
,它会这样做。
虽然编译器在第一种情况下成功警告我,为什么它在第二种情况下无法识别。 为什么这种行为改变? 如果我将static
字段更改为instance
字段,并使用Demo
实例访问它,则行为相同。
我认为这种行为可能已在JLS中指定,因此我浏览了主题定义分配 ,但没有找到与此问题相关的任何内容。 任何人都可以解释行为的变化吗? 如果可能的话,我正在寻找一些与JLS有关联的强点吗?
除此之外,为什么编译器首先只向我显示警告 ,因为我认为出于与上述相同的原因,方法调用肯定会在运行时抛出NPE ,因为字段无法更改? 为什么不向我显示编译器错误? 我是否期望编译器太多,因为似乎很明显, str.length()
的运行时结果不能比NPE
?
很抱歉错过了之前:
我正在使用Eclipse Juno ,在Ubuntu 12.04上使用OpenJDK 7 。
我不确定在100%,但在第二种情况下,你有最终字段对抗局部变量,有可能为这个最终字段分配一些直接在静态的值(或实例块取决于变量是否为静态)初始化块:
class Demo {
...
static {
str = "some text";
}
...
}
所以编译器不会给你警告。
哇! 原来,这是日食的具体问题。 当我使用以下代码编译代码时:
javac -Xlint:all Demo.java
它没有对任何案件发出任何警告。 所以,我回到eclipse检查为这种情况启用的任何设置,并找到了一个。
在Windows - > 首选项 - > Java - > 编译器 - > 错误/警告中 ,在Null Analysis下 ,我可以更改Eclipse编译器应该处理Null指针访问的方式。 我可以将其设置为 - 忽略 , 错误或警告 。
现在看来这是一个完全愚蠢的问题。 对我感到羞耻。 :(
public static void main(String[] args) {
final String str = null;
System.out.println(str.length()); // Compiler Warning: NullPointerAccess
}
在这种情况下,str是一个局部变量,如果在初始化之前尝试对其执行任何操作,编译器将无法编译。 流分析完全不同,它将检查代码的流程,在代码流中,它检测到在执行length()操作时,局部变量str只能为null。
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)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.