Почему я не получаю NullPointerException? - программирование
Подтвердить что ты не робот

Почему я не получаю NullPointerException?

Возможный дубликат:
Статические поля в нулевой ссылке в Java

Я понимаю, что статические методы находятся на уровне класса. Поэтому я знаю, что мне не нужно создавать экземпляр для вызова статических методов. Но я также знаю, что я могу вызвать статический метод LIKE методом экземпляра. Здесь я запутался, потому что ожидал NullPointerException, вызывая статический метод из нулевого объекта (как в методе вызова экземпляра). Я бы очень признателен за некоторые объяснения, почему я был не в порядке ожидать здесь NullPointerException.

Вот пример кода:

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
    }
}
4b9b3361

Ответ 1

Вызов статического метода через экземпляр не требует, чтобы экземпляр был там. Пока компилятор может определить тип переменной, он делает эквивалентный вызов статически после оценки выражения sc и отбрасывая результат:

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

Из Спецификация языка Java:

Раздел 15.12.1

❖ Если форма Primary.NonWildTypeArgumentsopt Identifier, то имя этот метод является Идентификатором. Пусть T - тип первичного выражения. Класс или интерфейс для поиска - T, если T - тип класса или интерфейса, или верхняя граница T, если T является переменной типа.

Раздел 15.12.4.1:

  • Если второе производство для MethodInvocation, включающее Primary,, то есть два подслучая:

❖ Если режим вызова является статическим, то целевой ссылки нет. выражение Primary оценивается, но результат затем отбрасывается.

Ответ 2

Это какая-то ошибка дизайна от дизайнеров java. Вы должны вызвать статический метод для класса, потому что он принадлежит классу, а не объекту.

Вы можете немного узнать об этой проблеме в why-isnt-calling-a-static-method-by-way-of-an-instance-an-error-for-the-java-co

Самое смешное, было бы невозможно вызвать статический метод для переменной объекта, которая не была инициализирована. Но если объект инициализирован нулевым, все в порядке.

Я думаю, что это работает, потому что объект, хранящийся с этой переменной, предоставляет информацию типа через присваивание.

SampleClass sampleObject;
sampleObject.getSumStatic(2, 2) 

не будет компилироваться, потому что объект не инициализирован, поэтому информация о типе не задана в дереве синтаксиса java-компилятора.

Ответ 3

Java позволит вам получить доступ к статическому методу, основанному просто на ссылке, даже если эта ссылка null. Важно только тип ссылки.

Обычно вы должны использовать имя класса для вызова статических методов:

SampleClass.getSumStatic(2, 2);

Ответ 4

В качестве дополнительной заметки к dasblinkenlight (абсолютно правильному) отклику вы можете увидеть разницу в байт-коде Java, который генерируется (что вы можете видеть с помощью javap -c).

Рассмотрим следующий (более простой) класс:

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

И приложение, которое его использует:

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

Посмотрите на байт-код, сгенерированный для 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

Выполняя это (путем смещения, который является числовым столбцом в вышеприведенном выходе):

Инструкции при смещениях 0 и 1 загружают null, а затем сохраняют его в локальной переменной 1 (ex).

Команда со смещением 2 выполняет традиционный статический вызов: это инструкция invokestatic, которая вызывает Example.staticMethod(). Это не включает переменную экземпляра, как вы ожидаете.

Следующий вызов - это вызов статического метода в нашем экземпляре. Команда со смещением 5 загружает ex в стек (помните, что это значение равно null), но pop со смещением 6 немедленно отменяет это. Таким образом, invokestatic при смещении 7 ведет себя точно так же, как и значение смещения 2: нулевое значение не находится в стеке виртуальной машины, а метод, который должен быть вызван, скомпилирован в javac, независимо от того, какое значение ex есть.

В отличие от этого, виртуальный (то есть нестатический) метод подталкивает экземпляр в стек (смещение 10), а затем, когда он находится в стеке, выполняет инструкцию invokevirtual, которая смотрит virtualMethod() на экземпляр в найти метод для выполнения. Это шаг, на который вызывается a NullPointerException, так как этот поиск не может продолжаться. (Обратите внимание, что этот шаг не нужен в статическом случае, а также почему вызовы статических методов быстрее в наивной виртуальной машине.)

Ответ 5

Вы получаете доступ к статическому методу через переменную, которая строго типизирована на SampleClass. Во время компиляции метод разрешается как вызываемый непосредственно по определению класса (следовательно, это статический метод), поэтому он фактически разрешен.

В public static int getSumStatic нет неявного this, поэтому доступ к нулевому указателю отсутствует.

Ответ 6

Я думаю, что статические функции для экземпляра экземпляра класса в основном заменяются их статическими данными класса во время компиляции (они также не работают с наследованием, например, методом экземпляра).

Метод экземпляра пытается получить доступ к пустым методам и выдает исключение NullPointerException.