为什么在使用null引用访问静态时不会出现NullPointerException?

在下面的代码中,我们在null引用上获得i的值,尽管NPE不存在。

 public class Test { static int i = 10; Test getTest() { return null; } public static void main(String args[]) { Test t = new Test(); System.out.println(t.getTest()); System.out.println(t.getTest().i); } } 

产量

 null 10 

来自Java语言规范

Receiver Variable Is Irrelevant For static Field Access

以下程序演示了可以使用null引用来访问类(静态)变量而不会导致exception:

 class Test3 { static String mountain = "Chocorua"; static Test3 favorite(){ System.out.print("Mount "); return null; } public static void main(String[] args) { System.out.println(favorite().mountain); } } 

它编译,执行和打印:

 Mount Chocorua 

即使favorite()的结果为null,也不会抛出NullPointerException。 打印出“Mount”表明主要表达式确实在运行时完全评估,尽管事实上只使用其类型而不是其值来确定要访问的字段(因为字段山是静态的)。

即使在运行时评估主表达式(这是实例),但是它的值被丢弃,只考虑其类型。

非正式地说,你可以想到这一点

 System.out.println(t.getTest().i); 

相当于

 System.out.println(Test.i); 

因为i是静止的

这可能是最简单的答案。

严格来说,它们并不等同。 其实
getTest()但不使用其返回值
用于访问i字段,如下面的测试所示。

 public class Test { static int i = 10; Test getTest() { System.out.println("Method getTest() called!"); return null; } public static void main(String args[]) { Test t = new Test(); System.out.println(t.getTest()); System.out.println(t.getTest().i); } } 

我是一个静态变量,不需要实例来获取它的值。

生成的字节码如下所示:

 18 getstatic java.lang.System.out : java.io.PrintStream [24] 21 aload_1 [t] 22 invokevirtual experiments.Experiments.getTest() : experiments.Experiments [30] 25 pop 26 getstatic experiments.Experiments.i : int [10] 29 invokevirtual java.io.PrintStream.println(int) : void [38] 

如您所见,确实调用了t.getTest() (21,22),但未使用其结果(25)。 以静态方式访问字段i (26)。 Java字节码无法通过实例访问静态成员。 请注意,这意味着t.getTest().iTest.i 不是等效表达式! 在假设的语法中,等价物可能看起来像这样(使用我对这种语法直观的语义:

 System.out.println( {t.getTest(); return Test.i;} ); 

请注意,同样适用于现场testt.test.iTest.i不同。 虽然获得该字段不会产生任何副作用,并且本身不是一个有效的声明,但AspectJ仍然可以建议字段访问。

  Test t = new Test(); // initialize t 

这里t不是null因为它已被初始化。 因此,您不会得到NullPointerException

在下一种情况下,您期望NullPointerException因为t.getTest()返回null

 t.getTest().i; 

i是一个static变量,你不需要一个实例来访问静态变量,你可以直接访问它们。 因此,您也不会在此处获得NullPointerException

而且,

 System.out.println(i); // is an another way to access static i 

静态方法或变量不需要对对象的引用。 您可以调用它甚至引用该对象为null。

更具体,

在访问Static变量时,编译器将生成与该static对应的getStatic指令,并将用于访问该static 。 因此静态是实例独立的,它们通过字段/方法仅使用运行时常量池的索引来解析,该索引稍后将用于求解字段引用位置。

有关详细信息,请参阅以下答案: https : //stackoverflow.com/a/21047440/1686291

简单来说,编译器从类def中获取静态,而不是从对象中获取静态。 这就是为什么你可以用t.getTest().i替换t.getTest().i ,它将是相同的。