为什么Java编译器允许通过null对象进行静态变量访问?

Not*_*bug 27 java static javac nullpointerexception

我指着一些技巧并遇到了这个.在以下代码中:

public class TestClass1 {

    static int a = 10;

    public static void main(String ar[]){
        TestClass1 t1 = null ;
        System.out.println(t1.a); // At this line
    }
}
Run Code Online (Sandbox Code Playgroud)

t1对象是null.为什么这段代码不抛NullPointerException

我知道这不是访问static变量的正确方法,但问题是关于NullPointerException.

Sur*_*tta 27

调用静态成员或方法时不需要实例.

由于静态成员属于类而不是实例.

空引用可用于访问类(静态)变量而不会导致异常.

http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#d5e19846

如果你看到这个例子(参见规范中的完整示例)

 public static void main(String[] args) {
        System.out.println(favorite().mountain); //favorite() returns null
    }
Run Code Online (Sandbox Code Playgroud)

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


hig*_*aro 11

要向当前答案添加一些其他信息,如果您使用以下方法反汇编类文件:

javap -c TestClass1
Run Code Online (Sandbox Code Playgroud)

你会得到:

Compiled from "TestClass1.java"
public class TestClass1 extends java.lang.Object{
static int a;

public TestClass1();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   aconst_null
   1:   astore_1
   2:   getstatic   #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   5:   aload_1
   6:   pop
   7:   getstatic   #3; //Field a:I
   10:  invokevirtual   #4; //Method java/io/PrintStream.println:(I)V
   13:  return

static {};
  Code:
   0:   bipush  10
   2:   putstatic   #3; //Field a:I
   5:   return
}
Run Code Online (Sandbox Code Playgroud)

在这里,您可以看到通过getstatc指令在第7行完成对静态字段的访问.每当通过代码访问静态字段时,getstatic将在.class程序文件中生成相应的指令.

*static指令具有这样的特殊性:在调用它们之前,它们不需要在堆栈中引用对象实例(例如,在堆栈中需要对象引用的invokevirtual),它们仅使用just解析字段/方法运行时常量池的索引,稍后将用于求解字段引用位置.

这是警告"静态字段应该以静态方式访问"的技术原因,一些IDE在您编写时会向您抛出t1.a,因为对象实例不需要解析静态字段.