为什么在使用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().i
和Test.i
不是等效表达式! 在假设的语法中,等价物可能看起来像这样(使用我对这种语法直观的语义:
System.out.println( {t.getTest(); return Test.i;} );
请注意,同样适用于现场test
: t.test.i
与Test.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
,它将是相同的。
- 在String for Java中每60个字符后添加换行符
- Maven离线 – mvn-plugins的问题
- 没有使用log4j配置
- spring batch exception无法构造java.util.Map $ Entry
- Java:如何防止EntityResolver中的’systemId’#resolveEntity(String publicId,String systemId)从绝对化到当前工作目录
- HTTP Node.js Java API
- java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver在java中不再起作用。 如何解决这个问题?
- 静态与实例变量:差异?
- 如何在java中构建url?