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

2022-09-01 15:41:28

我指出了一些技巧,并遇到了这个问题。在下面的代码中:

public class TestClass1 {

    static int a = 10;

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

t1对象是 。为什么这个代码没有抛出?nullNullPointerException

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


答案 1

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

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

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

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
    }

即使 favorite() 的結果為 null,也不會擠出 NullPointerException。打印的“Mount”表明 Main 表达式在运行时确实被完全计算,尽管仅使用其类型而不是其值来确定要访问的字段(因为字段山是静态的)。


答案 2

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

javap -c TestClass1

您将获得:

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
}

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

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

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


推荐