Why I am not getting NullPointerException? [duplic

2019-01-27 13:21发布

问题:

Possible Duplicate:
Static fields on a null reference in Java

I understand that static methods are on class level. So I am aware that I do not need to create instance to call static methods. But I am also aware that I can call static method LIKE an instance method. This is where I am confused, because I was expecting a NullPointerException while calling the static method from the null object (as in calling instance method). I would really appreciate some explanation on why I was wrong to expect a NullPointerException here.

Here is the sample code:

public class SampleClass {

    public static int getSumStatic(int x, int y){
        return x+y;
    }

    public int getDifferenceInstance(int x, int y){
        return x-y;
    }
}

public class TestClass {

    public static void main (String[] args){        
    SampleClass sc=null;

    System.out.println(SampleClass.getSumStatic(2, 2)); //as expected

    //I was expecting NullPointerException in the next line, since I am accessing null object
    System.out.println(sc.getSumStatic(4,5)); //static method , executes perfectly  

    System.out.println(sc.getDifferenceInstance(6,4));//throws NullPointerException
    }
}

回答1:

Calling a static method through an instance does not require the instance to be there. As long as the compiler is able to determine the type of the variable, it makes the equivalent call statically after evaluating the sc expression and discarding the result:

System.out.println(SampleClass.getSumStatic(4,5));

From the Java Language Specification:

Section 15.12.1

❖ If the form is Primary.NonWildTypeArgumentsopt Identifier, then the name of the method is the Identifier. Let T be the type of the Primary expression. The class or interface to be searched is T if T is a class or interface type, or the upper bound of T if T is a type variable.

Section 15.12.4.1:

  1. If the second production for MethodInvocation, which includes a Primary, is involved, then there are two subcases:

❖ If the invocation mode is static, then there is no target reference. The expression Primary is evaluated, but the result is then discarded.



回答2:

This is some kind of a design mistake from the java designers. You should call a static method on the class, because it belongs to the class and not to an object.

You can see a bit about this issue at why-isnt-calling-a-static-method-by-way-of-an-instance-an-error-for-the-java-co

The funny thing, it would not be possible to call a static method on an object variable which has not been initialized. But if the object is initialized with null, everything is fine.

I think this works because the object stored with this variable provide the type information through the assignment.

SampleClass sampleObject;
sampleObject.getSumStatic(2, 2) 

won't compile because the object is not initialized, therefore no type information is set in the syntax tree of the java compiler.



回答3:

Java will allow you to access the static method based simply on the reference, even when the reference is null. Only the type of the reference is important.

You should generally use the class name to call static methods:

SampleClass.getSumStatic(2, 2);


回答4:

As an additional note to dasblinkenlight's (absolutely correct) response, you can see the difference in the Java bytecode that gets generated (which you can see readably with javap -c).

Consider the following (simpler) class:

public class Example {
  public static void staticMethod() {}
  public void virtualMethod() {}
}

And an application which uses it:

public class ExampleApplication {
  public static void main(String[] args) {
    Example ex = null;
    Example.staticMethod();
    ex.staticMethod();
    ex.virtualMethod();
  }
}

Let's look at the bytecode generated for ExampleApplication.main(String[]):

public static void main(java.lang.String[]);
  Code:
   0:   aconst_null
   1:   astore_1
   2:   invokestatic    #2; //Method Example.staticMethod:()V
   5:   aload_1
   6:   pop
   7:   invokestatic    #2; //Method Example.staticMethod:()V
   10:  aload_1
   11:  invokevirtual   #3; //Method Example.virtualMethod:()V
   14:  return

Stepping through this (by offset, which is the numeric column in the above output):

The instructions at offsets 0 and 1 load null and then store it into local variable 1 (ex).

The instruction at offset 2 does a traditional static call: it's an invokestatic instruction which invokes Example.staticMethod(). This doesn't involve the instance variable, as you expect.

Next up is the call to the static method on our instance. The instruction at offset 5 loads ex onto the stack (remember that this is null), but the pop at offset 6 undoes this immediately. So the invokestatic at offset 7 behaves exactly the same as the one at offset 2: the null value isn't on the VM stack, and the method to be invoked is compiled in by javac, regardless of what the value of ex is.

By contrast, a virtual (i.e. non-static) method pushes the instance onto the stack (offset 10), and then while it's on the stack, executes an invokevirtual instruction which looks up virtualMethod() on the instance to find the method to execute. This is the step where a NullPointerException is thrown, since that lookup cannot proceed. (Note that this step is unnecessary in the static case, which is also why static method calls are faster in a naive VM.)



回答5:

You are accessing a static method through a variable which is strongly typed to SampleClass. During compilation the method is resolved as being called directly on the class definition (hence it's a static method) so it is actually resolved.

There is no implicit this in public static int getSumStatic so there is no access to a null pointer.



回答6:

I think static functions for a class instance instance are basically replaced with their static class info at compile time(they also don't work with inheritance like instance methods).

Instance method tries to access null's methods and throws a NullPointerException.