我在互联网上找到了这个代码,并希望有人向我解释......
public class Foo {
static int fubar = 42;
public static void main(String[] args) {
System.out.println(((Foo) null).fubar);
}
}
此代码编译并正常工作,输出结果42
。
如何在不抛出fubar
的情况下从null
访问变量NullPointerException
?
答案 0 :(得分:10)
实际上并没有在null
上查找字段,因为静态方法和字段不需要实例。强制类型表达式Foo
的类型,而fubar
是Foo
上已知的静态字段,因此编译器和JVM没有问题。
通常,您可以通过说Foo.fubar
来访问该字段。但是,Java足以提供一个快捷方式:如果您尝试访问给定类型的实例表达式上的静态字段或方法,它会将其视为您所说的[SomeType].theField
。这就是这里发生的事情。
答案 1 :(得分:4)
由于字段fubar
已声明为static
,因此实际上只有一个字段名为Foo.fubar
。 Foo
的每个实例共享此副本。当您从Foo
对象访问此字段时,Java不会尝试按照对象引用来查找它。相反,它在特定定义的位置查找对象,可以独立于任何引用进行访问。因此,如果您尝试查找null
对象的此字段,则可以在不引起任何NullPointerException
的情况下执行此操作,因为永远不会引用该对象。
编辑:字节码肯定是有序的!鉴于此源文件:
public class Foo {
static int fubar;
public Foo() {
((Foo)null).fubar = 137;
}
}
这是生成的字节码:
0: aload_0
1: invokespecial #1; //Method Object."<init>":()V
4: aconst_null
5: checkcast #2; //class Foo
8: pop
9: sipush 137
12: putstatic #3; //Field fubar:I
15: return
请注意,第12行使用putstatic
操作码,该操作码将值存储到static
字段中。它没有引用任何类型的接收器对象。实际上,如果您注意到,生成的字节码(第4-8行)会执行null
到Foo
的转换,但会立即发出pop
操作码以将其弹出堆。它从未在字节码中的任何其他位置引用。
答案 2 :(得分:1)
fubar是一个静态成员,这个null的强制转换只是强调了在编译时而不是运行时分配静态变量。访问静态变量的更常见和等效的方法是Foo.fubar