Ответ 1
Это потому, что greet()
является статическим методом. Итак,
((Null)null).greet();
эквивалентно
Null.greet()
В чем причина выхода? Я знаю, что он печатает Hello World
, но не знаю почему, поскольку он должен давать NullPointerException
.
public class Null
{
public static void greet()
{
System.out.println("Hello World");
}
public static void main(String[] args)
{
((Null)null).greet();
}
}
Это потому, что greet()
является статическим методом. Итак,
((Null)null).greet();
эквивалентно
Null.greet()
Поскольку greet
является статическим методом, экземпляр класса не требуется (и не используется...) для его вызова.
Выражение ((Null)null)
не разыменовывает null
, оно просто служит определением типа, используемым для доступа к статическому методу.
Когда мы пытаемся использовать ссылку на объект с нулевым значением, выдается NullPointerException
. Итак, в вашем примере вы можете подумать, что метод greet()
успешно вызывается из нулевого объекта.
Но внимательно посмотрите на сигнатуру метода, перед ней стоит модификатор static
. Если вы вызываете статический метод для объекта с нулевой ссылкой, вы не получите исключение, и код будет выполняться без каких-либо исключений. Это потому, что статические методы являются методами класса, а не методом экземпляра.
Поэтому, когда вы компилируете свой код, ((Null)null).greet()
просто преобразуется в Null.greet()
.
Для простоты рассмотрите код ниже:
Null obj1 = null;
Null obj2 = new Null();
obj1.greet();
obj2.greet();
Поскольку greet()
является здесь статическим методом, во время этого вызова компилятор метода просто игнорирует, есть ли внутри объекта что-либо созданное или нет. Он будет просто скомпилирован как Null.greet()
для obj1
и obj2
.
Однако попробуйте удалить модификатор static
из метода. Вы обнаружите, что NullPointerException
вы ожидали.
Это допустимое поведение, поскольку:
((Null)null).greet();
будет похоже на вызов статического метода greet
в классе Null
.
Это даже показано как правильное поведение в примере 15.11.1-2. Receiver Variable Is Irrelevant For static Field Access
JLS:
Следующая программа демонстрирует, что нулевая ссылка может использоваться для доступа к переменной (статической) класса без исключения:
class Test3 {
static String mountain = "Chocorua";
static Test3 favorite(){
System.out.print("Mount ");
return null;
}
public static void main(String[] args) {
System.out.println(favorite().mountain);
}
}
И объяснение того, что происходит, почему оно компилируется и печатается Mount Chocorua
:
Даже если результатом
favorite()
являетсяnull
,NullPointerException
не выбрасывается. То, что "Mount" напечатано, демонстрирует, что первичное выражение действительно полностью вычисляется во время выполнения, несмотря на тот факт, что только его тип, а не его значение, используется для определения того, к какому полю обращаться (поскольку полеmountain
является статическим).
Так что в вашем случае это только тип выражения ((Null)null)
, который оценивается как Null
, который используется для определения, какой метод вызывать (в классе Null
есть статический метод greet
, поэтому он не является проблема).