Первое, что нужно понять, это как работает экземпляр объекта на уровне байт-кода.
Как объяснено в 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.