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

时间:2021-10-13 01:44:14

This question already has an answer here:

这个问题在这里已有答案:

I was pointing some tricks and came across this. In following code:

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

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 object is null. Why this code is not throwing NullPointerException?

t1对象为null。为什么这段代码没有抛出NullPointerException?

I know this is not proper way to access static variables but question is about NullPointerException.

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

7 个解决方案

#1


10  

To add some additional info to the current answers, if you disassemble your class file using:

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

javap -c TestClass1

You'll get:

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
}

Here you can see that the access to the static field is done in line 7 by the getstatc instruction. Whenever a static field is accessed through code, a corresponding getstatic instruction will be generated in the .class program file.

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

*static instructions have the particularity that they don't requiere a reference to the object instance to be in the stack prior to calling them (like, for example invokevirtual which does require an object ref in the stack), they resolve the field/method using just an index to the run time constant pool that will be later used to solve the field reference location.

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

That's a technical reason for the warning "The static field should be accessed in a static way" that some IDEs will throw at you when you write t1.a, because the object instance is unnecessary to resolve the static field.

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

#2


27  

There is no need for an instance while invoking static member or method.

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

Since static members belongs to class rather than instance.

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

A null reference may be used to access a class (static) variable without causing an exception.

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

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

If you see the example (see full example in spec)

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

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

Even though the result of favorite() is null, a NullPointerException is not thrown. That "Mount " is printed demonstrates that the Primary expression is indeed fully evaluated at run time, despite the fact that only its type, not its value, is used to determine which field to access (because the field mountain is static).

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

#3


2  

What is the reason of NullPointerException in a code like that:

在这样的代码中NullPointerException的原因是什么:

  TestClass t = null;
  t.SomeMethod();

If SomeMethod is an instance method it'll do something with standard this reference:

如果SomeMethod是一个实例方法,它将使用标准的引用做一些事情:

  public void SomeMethod() {
    // Here we'll have a NullPointerException (since "this" is null)
    this.SomeField = ... // <- We usually omit "this" in such a code
  }

Since this is null we'll have NullPointerException. If method, field etc. is static it's guaranteed the absense of this reference, so there'll be no NullPointerException

由于这是null,我们将有NullPointerException。如果method,field等是静态的,那么保证了这个引用的缺失,所以不会有NullPointerException

  public static void SomeStaticMethod() {
    // You can't put "this.SomeField = ..." here, because the method is static
    // Ans since you can't address "this", there's no reason for NullPointerException
    ...
  }

  ...

  TestClass t = null; 
  // Equal to "TestClass.SomeStaticMethod();"
  t.SomeStaticMethod(); // <- "this" is null, but it's not addressed

#4


2  

The static members are shared by all the instances of a class and not reallocated separately to each instance. These are loaded by the classloader moment the class is encountered for the first time. A related SO question

静态成员由类的所有实例共享,而不是单独重新分配给每个实例。这些是第一次遇到类时类加载器加载的。一个相关的SO问题

Please do keep in mind these static members do not belong to a specific class instance. More info here. I have included a small code snippet for reference:

请记住,这些静态成员不属于特定的类实例。更多信息在这里。我已经提供了一个小代码片段供参考:

@NotThreadSafe
public class Test {

  // For all practical purpuose the following block will be only executed once at class load time.
  static {
   System.out.println("Loaded by the classloader : " + Test.class.getClassLoader());
  }

  // Keeps track of created instances.
  static int instanceCount;


  // A simple constructor that increments instance count.
  public Test(){
   Test.instanceCount++;
   System.out.println("instance number : " + instanceCount);
  }

  public static void main(String[] args) {
   System.out.println("Instaintiating objects");
   new Test(); new Test();
  }

  // Where would you expect this line to get printed? 
  // i.e last statement on the console or somewhere in the middle :)
  static {
    System.out.println("It should be printed at the end or would it?");
   }
 }

#5


1  

t1.a is equivalent to TestClass1.a in this case, since a is a static member (not an instance member). The compiler looks at the declaration of t1 to see what type it is, and then just treats it as if you had used the type name. The value of t1 is never checked. (But if it were a method call, like method(args).a, I think the method would be called. But the return value would be thrown away, and never checked.) (Edit: I've verified that method(args) is called, but no exception is thrown if the function result is null.)

在这种情况下,t1.a等同于TestClass1.a,因为a是静态成员(不是实例成员)。编译器查看t1的声明以查看它的类型,然后将其视为使用类型名称。永远不会检查t1的值。 (但如果它是一个方法调用,比如方法(args).a,我认为该方法将被调用。但返回值将被丢弃,并且从未检查过。)(编辑:我已经验证了该方法(args) )被调用,但如果函数结果为null,则不会抛出异常。)

#6


0  

Since a is static compiler converts it to TestClass1.a. For non-static variables it would throw NullPointerException.

由于是静态编译器将其转换为TestClass1.a。对于非静态变量,它会抛出NullPointerException。

#7


-1  

Any Static member can be accessed by Directly class name as TestClass1.a no need of instance for it

可以通过Directly类名称访问任何静态成员作为TestClass1.a,不需要实例

  System.out.println(TestClass1 .a);

output: 10

#1


10  

To add some additional info to the current answers, if you disassemble your class file using:

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

javap -c TestClass1

You'll get:

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
}

Here you can see that the access to the static field is done in line 7 by the getstatc instruction. Whenever a static field is accessed through code, a corresponding getstatic instruction will be generated in the .class program file.

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

*static instructions have the particularity that they don't requiere a reference to the object instance to be in the stack prior to calling them (like, for example invokevirtual which does require an object ref in the stack), they resolve the field/method using just an index to the run time constant pool that will be later used to solve the field reference location.

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

That's a technical reason for the warning "The static field should be accessed in a static way" that some IDEs will throw at you when you write t1.a, because the object instance is unnecessary to resolve the static field.

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

#2


27  

There is no need for an instance while invoking static member or method.

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

Since static members belongs to class rather than instance.

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

A null reference may be used to access a class (static) variable without causing an exception.

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

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

If you see the example (see full example in spec)

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

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

Even though the result of favorite() is null, a NullPointerException is not thrown. That "Mount " is printed demonstrates that the Primary expression is indeed fully evaluated at run time, despite the fact that only its type, not its value, is used to determine which field to access (because the field mountain is static).

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

#3


2  

What is the reason of NullPointerException in a code like that:

在这样的代码中NullPointerException的原因是什么:

  TestClass t = null;
  t.SomeMethod();

If SomeMethod is an instance method it'll do something with standard this reference:

如果SomeMethod是一个实例方法,它将使用标准的引用做一些事情:

  public void SomeMethod() {
    // Here we'll have a NullPointerException (since "this" is null)
    this.SomeField = ... // <- We usually omit "this" in such a code
  }

Since this is null we'll have NullPointerException. If method, field etc. is static it's guaranteed the absense of this reference, so there'll be no NullPointerException

由于这是null,我们将有NullPointerException。如果method,field等是静态的,那么保证了这个引用的缺失,所以不会有NullPointerException

  public static void SomeStaticMethod() {
    // You can't put "this.SomeField = ..." here, because the method is static
    // Ans since you can't address "this", there's no reason for NullPointerException
    ...
  }

  ...

  TestClass t = null; 
  // Equal to "TestClass.SomeStaticMethod();"
  t.SomeStaticMethod(); // <- "this" is null, but it's not addressed

#4


2  

The static members are shared by all the instances of a class and not reallocated separately to each instance. These are loaded by the classloader moment the class is encountered for the first time. A related SO question

静态成员由类的所有实例共享,而不是单独重新分配给每个实例。这些是第一次遇到类时类加载器加载的。一个相关的SO问题

Please do keep in mind these static members do not belong to a specific class instance. More info here. I have included a small code snippet for reference:

请记住,这些静态成员不属于特定的类实例。更多信息在这里。我已经提供了一个小代码片段供参考:

@NotThreadSafe
public class Test {

  // For all practical purpuose the following block will be only executed once at class load time.
  static {
   System.out.println("Loaded by the classloader : " + Test.class.getClassLoader());
  }

  // Keeps track of created instances.
  static int instanceCount;


  // A simple constructor that increments instance count.
  public Test(){
   Test.instanceCount++;
   System.out.println("instance number : " + instanceCount);
  }

  public static void main(String[] args) {
   System.out.println("Instaintiating objects");
   new Test(); new Test();
  }

  // Where would you expect this line to get printed? 
  // i.e last statement on the console or somewhere in the middle :)
  static {
    System.out.println("It should be printed at the end or would it?");
   }
 }

#5


1  

t1.a is equivalent to TestClass1.a in this case, since a is a static member (not an instance member). The compiler looks at the declaration of t1 to see what type it is, and then just treats it as if you had used the type name. The value of t1 is never checked. (But if it were a method call, like method(args).a, I think the method would be called. But the return value would be thrown away, and never checked.) (Edit: I've verified that method(args) is called, but no exception is thrown if the function result is null.)

在这种情况下,t1.a等同于TestClass1.a,因为a是静态成员(不是实例成员)。编译器查看t1的声明以查看它的类型,然后将其视为使用类型名称。永远不会检查t1的值。 (但如果它是一个方法调用,比如方法(args).a,我认为该方法将被调用。但返回值将被丢弃,并且从未检查过。)(编辑:我已经验证了该方法(args) )被调用,但如果函数结果为null,则不会抛出异常。)

#6


0  

Since a is static compiler converts it to TestClass1.a. For non-static variables it would throw NullPointerException.

由于是静态编译器将其转换为TestClass1.a。对于非静态变量,它会抛出NullPointerException。

#7


-1  

Any Static member can be accessed by Directly class name as TestClass1.a no need of instance for it

可以通过Directly类名称访问任何静态成员作为TestClass1.a,不需要实例

  System.out.println(TestClass1 .a);

output: 10