Подтвердить что ты не робот

Окончательный порядок инициализации полей

Вот код, который вызывает статический метод A.f() для класса, который еще не инициализирован. Может ли кто-нибудь объяснить поведение этого кода в терминах JLS?

class A {
    final static Object b = new B();
    final static int S1 = 1;
    final static Integer S2 = 2;

    static void f() {
        System.out.println(S1);
        System.out.println(S2);
    }
}

class B {
    static {
        A.f();
    }
}

public class App
{
    public static void main( String[] args )
    {
        A.f();
    }
}

Вывод:

1
null
1
2
4b9b3361

Ответ 1

A.f() в App.main() запускает инициализацию класса A.

Все постоянные переменные инициализируются. Единственная постоянная переменная S1, которая теперь 1.

Затем остальные статические поля инициализируются в текстовом порядке. b - это первое поле, которое запускает инициализацию класса b, которое, в свою очередь, вызывает A.f(). S2 просто null, потому что он еще не инициализирован. Инициализация b завершена. И последнее, но не менее важное: S2 инициализируется объектом Integer 2.

S2 не является постоянной переменной, потому что это не примитивный тип int, а ссылочный тип Integer. S2 = 2; является сокращением автоматического бокса для S2 = Integer.valueOf(2);.

Если декларатор в объявлении поля имеет инициализатор переменной, то декларатор имеет семантику присвоения (§15.26) объявленной переменной.

[...]

Обратите внимание, что поля static, которые являются постоянными переменными (§4.12.4), инициализируются перед другими полями static (§12.4.2). Это также применяется в интерфейсах (§9.3.1). Такие поля никогда не будут иметь начальных значений по умолчанию (§4.12.5) даже при использовании коварных программ.

8.3.2. Инициализация поля

Постоянная переменная является переменной final примитивного типа или типа String, которая инициализируется константным выражением (§15.28). Является ли переменная постоянной переменной или нет, может иметь последствия для инициализации класса (§12.4.1), бинарной совместимости (§13.1, §13.4.9) и определенного назначения (§16 (Определенное назначение)).

4.12.4. final Переменные

Постоянное выражение представляет собой выражение, обозначающее значение примитивного типа или String, которое не завершается внезапно и составлено с использованием только следующего:

  • Литералы примитивного типа и литералов типа String

[...]

15.28. Константные выражения

Для каждого класса или интерфейса C существует уникальная блокировка инициализации LC. Отображение от C до LC остается на усмотрение реализации виртуальной машины Java. Процедура инициализации C следующая:

[...]

  1. В противном случае запишите тот факт, что инициализация объекта Class для C выполняется текущим потоком и отпустите LC.

    Затем инициализируйте поля static C, которые являются постоянными переменными (§4.12.4, §8.3.2, §9.3.1).

[...]

  1. Затем выполните либо инициализаторы переменной класса, либо статические инициализаторы класса, или инициализаторы поля интерфейса, в текстовом порядке, как если бы они были одним блоком.

12.4.2. Подробная процедура инициализации

Каждая переменная в программе должна иметь значение перед его значением:

  • Каждая переменная класса, переменная экземпляра или компонент массива инициализируется значением по умолчанию при его создании (§15.9, §15.10.2):

    [...]

    • Для всех ссылочных типов (§4.3) значение по умолчанию null.

4.12.5. Начальные значения переменных

Ответ 2

Кажется, что этот вопрос не относится к JLS, но мы имеем дело с JVMS.

На этапе Linking перед процессом resolution есть Подготовить подкласс, который включает в себя:

создание статических полей для класса или интерфейса и инициализация такие поля соответствуют значениям по умолчанию

и более:

явные инициализаторы для статических полей выполняются как часть инициализация (§5.5), а не подготовка

а инициализация включает в себя:

Выполнение любой из инструкций Java Virtual Machine new

Инициализация примитивных типов включает в себя запись их начального значения. Для полей ссылочного типа их значение по умолчанию равно null, потому что перед Resolution подкласс jvm не "знает", какой класс связан с соответствующим символьным именем ссылки класса.