Байт-код конструктора - PullRequest
       87

Байт-код конструктора

0 голосов
/ 09 ноября 2018

Руководство ASM рассказывает о конструкторах:

package pkg;
public class Bean {
  private int f;
  public int getF() {
      return this.f;
  }
  public void setF(int f) {
      this.f = f;
  }
}

Класс Bean также имеет открытый конструктор по умолчанию, который генерируется компилятором, так как явный конструктор не был определен программистом Этот открытый конструктор по умолчанию генерируется как Bean() { super(); }. Байт-код этого конструктора является следующее:

ALOAD 0
INVOKESPECIAL java/lang/Object <init> ()V
RETURN

Первая инструкция помещает this в стек операндов. Второй инструкция извлекает это значение из стека и вызывает <init> метод, определенный в классе Object. Это соответствует super() вызов, то есть вызов конструктора суперкласса, Object. Вы Здесь можно увидеть, что конструкторы по-разному называются в скомпилированном и исходные классы: в скомпилированных классах они всегда называются <init>, в то время как в исходных классах у них есть имя класса, в котором они определены. Наконец, последняя инструкция возвращается вызывающей стороне.

Как значение this уже известно JVM до первой инструкции конструктора?

Ответы [ 2 ]

0 голосов
/ 09 ноября 2018

Первое, что нужно понять, это как работает экземпляр объекта на уровне байт-кода.

Как объяснено в JVMS, §3.8. Работа с экземплярами класса :

Экземпляры класса виртуальной машины Java создаются с использованием инструкции new виртуальной машины Java. Напомним, что на уровне виртуальной машины Java конструктор отображается как метод с именем, предоставленным компилятором <init>. Этот специально названный метод известен как метод инициализации экземпляра ( §2.9 ). Для данного класса могут существовать несколько методов инициализации экземпляра, соответствующих нескольким конструкторам. Как только экземпляр класса создан и его переменные экземпляра, включая переменные класса и все его суперклассы, инициализированы в их значения по умолчанию, вызывается метод инициализации экземпляра нового экземпляра класса. Например:

   Object create() {
       return new Object();
   }

компилируется в:

   Method java.lang.Object create()
   0   new #1              // Class java.lang.Object
   3   dup
   4   invokespecial #4    // Method java.lang.Object.<init>()V
   7   areturn

Таким образом, вызов конструктора через invokespecial разделяет поведение передачи this в качестве первого аргумента с invokevirtual.

Однако следует подчеркнуть, что ссылка на неинициализированную ссылку обрабатывается специально, поскольку вам не разрешено использовать ее до вызова конструктора (или супер-конструктора, когда вы находитесь внутри конструктора). Это обеспечивается проверяющим.

JVMS, § 4.10.2.4. Методы инициализации экземпляра и вновь созданные объекты :

… Метод инициализации экземпляра ( §2.9 ) для класса myClass видит новый неинициализированный объект в качестве аргумента this в локальной переменной 0. До того, как этот метод вызовет другой метод инициализации экземпляра myClass или его прямой суперкласс на this, единственная операция, которую метод может выполнить на this, - это присвоение полей, объявленных в myClass.

При выполнении анализа потока данных для методов экземпляра верификатор инициализирует локальную переменную 0, чтобы она содержала объект текущего класса, или, например, методы инициализации, локальная переменная 0 содержит специальный тип, указывающий неинициализированный объект. После того, как соответствующий метод инициализации экземпляра (из текущего класса или его прямого суперкласса) вызывается для этого объекта, все вхождения этого специального типа в модели верификатора стека операндов и в массиве локальной переменной заменяются текущим типом класса. Верификатор отклоняет код, который использует новый объект до его инициализации или который инициализирует объект более одного раза. Кроме того, он гарантирует, что при каждом нормальном возврате метода вызывается метод инициализации экземпляра либо в классе этого метода, либо в прямом суперклассе.

Точно так же специальный тип создается и помещается в модель верификатора стека операндов в результате выполнения инструкции виртуальной машины Java new . Специальный тип указывает инструкцию, с помощью которой был создан экземпляр класса, и тип созданного неинициализированного экземпляра класса. Когда метод инициализации экземпляра, объявленный в классе экземпляра неинициализированного класса, вызывается для этого экземпляра класса, все экземпляры специального типа заменяются на предполагаемый тип экземпляра класса. Это изменение типа может распространяться на последующие инструкции по мере анализа потока данных.

Таким образом, код, создающий объект с помощью инструкции new , не может использовать его каким-либо образом до вызова конструктора, тогда как код конструктора может назначать поля только перед другим (this(…) или * 1050). *) был вызван конструктор (возможность, используемая внутренними классами для инициализации их ссылки на внешний экземпляр в качестве первого действия), но все еще не может ничего сделать с их неинициализированным this.

Это всеo не допускается, чтобы конструктор возвращался, когда this все еще находится в неинициализированном состоянии. Следовательно, автоматически сгенерированный конструктор несет требуемый минимум, вызывая супер-конструктор и возвращая (не существует неявного возврата на уровне байтового кода).

Обычно рекомендуется читать Спецификацию виртуальной машины Java® (соответственно Java 11 версия ) вместе с любой конкретной документацией или учебными пособиями по ASM.

0 голосов
/ 09 ноября 2018

На уровне JVM сначала объект выделяется, неинициализируется, а затем вызывается конструктор для этого объекта. Конструктор более или менее является экземпляром метода, выполняемого для неинициализированного объекта.

Даже на языке Java, this существует и имеет все свои поля в первой строке конструктора.

...