1. Обзор
В этом кратком руководстве мы сосредоточимся на типе возвращаемого значения для конструктора в Java.
Во-первых, мы познакомимся с тем, как работает инициализация объектов в Java и JVM. Затем мы копнем глубже, чтобы увидеть, как инициализация и присваивание объектов работают «под капотом».
2. Инициализация экземпляра
Начнем с пустого класса:
public class Color {}
Здесь мы собираемся создать экземпляр из этого класса и присвоить его некоторой переменной:
Color color = new Color();
После компиляции этого простого Java-фрагмента давайте взглянем на его байт-код с помощью команды javap -c
:
0: new #7 // class Color
3: dup
4: invokespecial #9 // Method Color."<init>":()V
7: astore_1
Когда мы создаем экземпляр объекта в Java, JVM выполняет следующие операции:
- Во- первых, он находит место в пространстве своего процесса для нового объекта.
- Затем JVM выполняет процесс инициализации системы. На этом этапе он создает объект в состоянии по умолчанию. Новый
код
операции в байт-коде фактически отвечает за этот шаг. - Наконец, он инициализирует объект с помощью конструктора и других блоков инициализатора. В этом случае код операции
invokespecial
вызывает конструктор.
Как показано выше, сигнатура метода для конструктора по умолчанию:
Method Color."<init>":()V
<init>
— это имя методов инициализации экземпляра в JVM . В данном случае <init>
— это функция, которая:
- ничего не принимает на вход (пустые скобки после имени метода)
- ничего не возвращает
(V
означаетvoid
)
Следовательно, возвращаемый тип конструктора в Java и JVM — void.
Еще раз взглянем на наше простое задание:
Color color = new Color();
Теперь, когда мы знаем, что конструктор возвращает void
, давайте посмотрим, как работает присваивание.
3. Как работает назначение
JVM — это виртуальная машина на основе стека. Каждый стек состоит из кадров стека . Проще говоря, каждый кадр стека соответствует вызову метода. На самом деле JVM создает фреймы вызовом нового метода и уничтожает их, когда они завершают свою работу:
Каждый кадр стека использует массив для хранения локальных переменных и стек операндов для хранения частичных результатов . Учитывая это, давайте еще раз посмотрим на байт-код:
0: new #7 // class Color
3: dup
4: invokespecial #9 // Method Color."<init>":()V
7: astore_1
Вот как работает задание:
- Новая инструкция создает экземпляр
Color и
помещает
его ссылку в стек операндов. `` - Код операции
dup
дублирует последний элемент в стеке операндов. - Invokespecial
берет
дубликат ссылки и использует ее для инициализации. После этого в стеке операндов остается только исходная ссылка. - astore_1 хранит
исходную
ссылку на индекс 1 массива локальных переменных. Префикс «а» означает, что сохраняемый элемент является ссылкой на объект, а «1» — это индекс массива.
Отныне второй элемент (индекс 1) в массиве локальных переменных является ссылкой на только что созданный объект . Поэтому мы не теряем ссылку, и присваивание действительно работает — даже когда конструктор ничего не возвращает!
4. Вывод
В этом кратком руководстве мы узнали, как JVM создает и инициализирует экземпляры нашего класса. Более того, мы увидели, как инициализация экземпляра работает «под капотом».
Для еще более подробного понимания JVM всегда полезно ознакомиться с ее спецификацией .