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

时间:2014-01-21 10:39:16

标签: java static nullpointerexception

在下面的代码中,我们在空引用上获得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

8 个答案:

答案 0 :(得分:7)

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

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);
    }
}

答案 1 :(得分:4)

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

答案 2 :(得分:4)

生成的字节码如下所示:

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;} );

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

答案 3 :(得分:2)

 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

答案 4 :(得分:2)

来自Java Language Specifications

Receiver Variable Is Irrelevant For static Field Access

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

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”表明主表达式确实在运行时完全评估,尽管事实上只使用其类型而不是其值来确定要访问的字段(因为字段山是静态的)。强>

即使主表达式(这是实例)在运行时被计算,也意味着,但是它的值被丢弃,只考虑它的类型。

答案 5 :(得分:1)

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

答案 6 :(得分:1)

更具体地说,

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

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

答案 7 :(得分:0)

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