Автор оригинала: Ali Dehghani.
1. Обзор
JVM использует два отличительных метода для инициализации экземпляров и классов объектов.
В этой краткой статье мы рассмотрим, как компилятор и среда выполнения используют методы и для целей инициализации.
2. Методы инициализации экземпляра
Давайте начнем с простого выделения и назначения объектов:
Object obj = new Object();
Если мы скомпилируем этот фрагмент и посмотрим на его байт-код через javap-c , мы увидим что-то вроде:
0: new #2 // class java/lang/Object 3: dup 4: invokespecial #1 // Method java/lang/Object."":()V 7: astore_1
Чтобы инициализировать объект , JVM вызывает специальный метод с именем . На жаргоне JVM этот метод является методом инициализации экземпляра . Метод является инициализацией экземпляра тогда и только тогда, когда:
- Он определен в классе
- Его имя < init>
- Он возвращает void
Каждый класс может иметь ноль или более методов инициализации экземпляра . Эти методы обычно соответствуют конструкторам в языках программирования на основе JVM, таких как Java или Kotlin.
2.1. Конструкторы и блоки инициализаторов экземпляров
Чтобы лучше понять , как компилятор Java переводит конструкторы в , рассмотрим другой пример:
public class Person { private String firstName = "Foo"; //private String lastName = "Bar"; // // { System.out.println("Initializing..."); } // public Person(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } // public Person() { } }
Это байт-код для этого класса:
public Person(java.lang.String, java.lang.String); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: aload_0 5: ldc #7 // String Foo 7: putfield #9 // Field firstName:Ljava/lang/String; 10: aload_0 11: ldc #15 // String Bar 13: putfield #17 // Field lastName:Ljava/lang/String; 16: getstatic #20 // Field java/lang/System.out:Ljava/io/PrintStream; 19: ldc #26 // String Initializing... 21: invokevirtual #28 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 24: aload_0 25: aload_1 26: putfield #9 // Field firstName:Ljava/lang/String; 29: aload_0 30: aload_2 31: putfield #17 // Field lastName:Ljava/lang/String; 34: return
Несмотря на то, что конструктор и блоки инициализатора разделены в Java, они находятся в одном и том же методе инициализации экземпляра на уровне байт-кода. На самом деле, это метод:
- Во-первых, инициализирует поля FirstName и LastName (индекс от 0 до 13)
- Затем он выводит что-то на консоль как часть блока инициализатора экземпляра (индекс с 16 по 21)
- И, наконец, он обновляет переменные экземпляра с помощью аргументов конструктора
Если мы создадим Человека следующим образом:
Person person = new Person("Brian", "Goetz");
Затем это переводится в следующий байт-код:
0: new #7 // class Person 3: dup 4: ldc #9 // String Brian 6: ldc #11 // String Goetz 8: invokespecial #13 // Method Person."":(Ljava/lang/String;Ljava/lang/String;)V 11: astore_1
На этот раз JVM вызывает другой метод с сигнатурой, соответствующей конструктору Java.
Ключевым моментом здесь является то, что конструкторы и другие инициализаторы экземпляров эквивалентны метод в мире JVM.
3. Методы инициализации класса
В Java статические блоки инициализатора полезны, когда мы собираемся инициализировать что-то на уровне класса:
public class Person { private static final Logger LOGGER = LoggerFactory.getLogger(Person.class); //// static { System.out.println("Static Initializing..."); } // omitted }
Когда мы компилируем предыдущий код, компилятор преобразует статический блок в метод инициализации класса на уровне байт-кода.
Проще говоря, метод является инициализацией класса тогда и только тогда, когда:
- Его зовут <клинт>
- Он возвращает void
Поэтому единственный способ создать метод в Java заключается в использовании статических полей и инициализаторов статических блоков.
JVM вызывает при первом использовании соответствующего класса. Таким образом, вызов происходит во время выполнения, и мы не можем видеть вызов на уровне байт-кода.
4. Заключение
В этой краткой статье мы увидели разницу между методами и в JVM. Метод используется для инициализации экземпляров объектов. Кроме того, JVM вызывает метод для инициализации класса при необходимости.
Чтобы лучше понять, как работает инициализация в JVM, настоятельно рекомендуется прочитать спецификацию JVM .