от
Я изучаю работу внутреннего класса и внутри его байт-кода, я отслеживал стек и не мог понять, почему вызывается getClass ()? Я нашел похожий вопрос для лямбда-функции, но не смог ее понять. Я попытался понять, что требуется для нулевой проверки, после JDK 8 его заменила статическая функция с именем requiredNoNull. Код:
class Outer{
      class Inner{
      }
      public static void main(String args[]){
            Outer.Inner obj = new Outer().new Inner();
      } 
}
ByteCode:
 public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class Outer$Inner
       3: dup
       4: new           #3                  // class Outer
       7: dup
       8: invokespecial #4                  // Method "":()V
      11: dup
      12: invokevirtual #5                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      15: pop
      16: invokespecial #6                  // Method Outer$Inner."":(LOuter;)V
      19: astore_1
             

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

3 Ответы

0 голосов
от
Насколько я понимаю, ответ @ Евгения абсолютно правильный. Я решил добавить объяснение простыми словами. Надеюсь, это кому-нибудь поможет. Ответ: вызовы
Object.getClass
использовались компилятором в JDK8 для генерации исключений NullPointerException, где это необходимо. В вашем примере эта проверка не нужна, поскольку
new Outer()
не может быть нулевым, но компилятор не был достаточно умен, чтобы определить это. В более поздних версиях JDK нулевые проверки были изменены, чтобы использовать более читаемые LiVyTJiJpOyfXOL3HL1YLD. Компилятор также был улучшен, чтобы оптимизировать ненужные проверки нуля. Объяснение: Рассмотрим код, подобный следующему:
class Outer{
      class Inner{
      }
      public static void main(String args[]){
            Outer.Inner obj = ((Outer) null).new Inner();
      } 
} 
Этот код генерирует исключение NullPointerException, как и должно быть. Проблема в том, что NPE логичен только с точки зрения Java. Конструкторы не существуют на уровне байтового кода. Компилятор генерирует байт-код, более или менее эквивалентный следующему псевдокоду:
class Outer {
    public static void main(String[] args) {
         Outer tmp = (Outer) null;
         Outer.Inner obj = new; //object created
         tmp."
0 голосов
от
Это скрытая проверка, ни больше, ни меньше. Тем не менее, в этом конкретном случае это на самом деле не нужно, и будущие
javac
это немного оптимизируют - посмотрите на пример ниже. Может быть, это объяснит проблему лучше (используя java-12, где этот хак
getClass
был заменен
Objects::requireNonNull
):
public class Outer {

    class Inner {

    }

    public void left() {
        Outer.Inner inner = new Outer().new Inner();
    }

    public void right(Outer outer) {
        Outer.Inner inner = outer.new Inner();
    }
}
Метод
left
будет компилировать к чему-то (вы можете посмотреть на байт-код самостоятельно), что не будет использовать
Objects::requireNonNull
, так как создание
Outer
происходит на месте и компилятор может точно сказать, что экземпляр
new Outer()
не
null
. С другой стороны, вы передаете
Outer
в качестве параметра, компилятор не может доказать, что переданный экземпляр не является нулевым, поэтому
Objects::requireNonNull
будет присутствовать в байт-коде.     
0 голосов
от
Поскольку определение класса Inner присутствует в Outer, JVM необходимо сначала загрузить Outer.     
...