Загрузка класса и инициализация: статическая конечная переменная Java

Example.java

public class Example {

    static final int i = 10;
    static int j = 20;
    static {
        System.out.println("Example class loaded and initialized");
    }
}

Use.java

import java.util.Scanner;
public class Use {
    public static void main(String args[]){
        Scanner sc = new Scanner(System.in);
        int ch = 1;
        while(ch != 0) {
            System.out.print("Enter choice: ");
            ch = sc.nextInt();

            if (ch == 1) {
                System.out.println("Example's i = " + Example.i);
            } else if(ch == 2){
                System.out.println("Example's j = " + Example.j);
            }
        }
    }
}

Когда я бегу с java -verbose:class Use и вводю как 1, тогда вывод будет 10, то есть постоянное значение i. Но Example класс еще не загружен. Однако, когда я ввожу ввод как 2, только тогда Example класс загружается в JVM, как видно из подробного вывода, а затем выполняется статический блок внутри примера, а также Значение j инициализировано и затем распечатано.

Мой запрос: Если для ввода 1, т.е. когда статическое конечное (постоянное) значение класса Example запрашивается в другом классе Use, то откуда берется это постоянное значение, если класс Example никогда не был загружен в JVM до тех пор? Когда и как статический final i был инициализирован и сохранен в памяти JVM?


person Harshit Rajput    schedule 22.08.2020    source источник
comment
Java может встроить конечные статические переменные в классы во время компиляции (javac). Это сделано для уменьшения количества классных нагрузок. Могу поклясться, что несколько дней назад я видел очень похожий вопрос. Я посмотрю, смогу ли я его найти.   -  person PiRocks    schedule 22.08.2020
comment
Старый вопрос, но есть информация об этом: stackoverflow.com/questions/3524150/   -  person PiRocks    schedule 22.08.2020
comment
@PiRocks, что имеет смысл. Спасибо, что ответили на этот вопрос и откопали старый вопрос и предоставили ссылку на него :) Итак, я правильно понимаю: что в скомпилированном файле Use.class 'System.out.println (Пример i = + Example.i)' есть на самом деле 'System.out.println (Пример i = + 10)' в форме байт-кода?   -  person Harshit Rajput    schedule 22.08.2020
comment
Правильный. Вы можете проверить это сами с javap -v -p path/to/Use.class   -  person PiRocks    schedule 22.08.2020
comment
Потрясающий! Проверил сейчас. Это действительно так. Спасибо друг :)   -  person Harshit Rajput    schedule 22.08.2020


Ответы (1)


Согласно спецификации языка Java раздел 12.4.1 (курсив добавлен):

Тип класса или интерфейса T будет инициализирован непосредственно перед первым появлением любого из следующих событий:

  • T - это класс, и создается экземпляр T.

  • Вызывается статический метод, объявленный T.

  • Назначено статическое поле, объявленное T.

  • Используется статическое поле, объявленное T, и поле не является постоянной переменной (§4.12.4).

постоянная переменная - это последняя переменная, которая инициализируется постоянное выражение. В вашем коде Example.i является постоянной переменной и поэтому не вызывает загрузку класса.

Итак, если класс не загружен, откуда берется значение com?

Спецификация языка требует, чтобы компилятор встроил его значение. Из раздела двоичной совместимости 13.1 :

  1. Ссылка на поле, которое является постоянной переменной (§4.12.4), должна быть разрешена во время компиляции до значения V, обозначенного инициализатором постоянной переменной.

    Если такое поле является статическим, то в коде двоичного файла не должно быть ссылки на поле, включая класс или интерфейс, в котором объявлено поле. Такое поле всегда должно выглядеть инициализированным (§12.4.2); начальное значение по умолчанию для поля (если оно отличается от V) никогда не должно соблюдаться.

person Joni    schedule 22.08.2020