Почему я не получаю 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
}
}
Ответы
Ответ 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.