Рубрики
Без рубрики

методы и в JVM

Смотрите, как компилятор и среда выполнения используют методы и для целей инициализации

Автор оригинала: 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 .