Класс или интерфейс T будет инициализирован сразу перед первым появлением следующих ситуаций:
- T является классом и создаётся экземпляр T.
- Вызывается статический метод, объявленный в T.
- Присваивается значение статическому полю, объявленному в T.
- Используется значение статического поля T, которое не является константой.
- T — это top level класс, и вызывается assert, лексически вложенный в T.
Перед инициализацией класса сначала инициализируются все его суперклассы (если они не были инициализированы до этого), так же как и любые суперинтерфейсы, которые объявляют методы по умолчанию (default-методы). Инициализация интерфейса не приводит к инициализации его суперинтерфейсов.
Обращение к статическому полю приводит к инициализации только того класса или интерфейса, который объявляет это поле, даже если обращение к этому полю было по имени дочернего класса, дочернего интерфейса или класса, реализующего интерфейс.
Обращение к некоторым методам класса Class из пакета java.lang.reflect тоже приводит к инициализации класса или интерфейса.
Класс или интерфейс не инициализируются ни в каких других случаях, кроме описанных выше.
Обратите внимание, что компилятор может генерировать синтетические методы по умолчанию в интерфейсе. Такие методы приводят к инициализации интерфейса, даже если исходных код не привёл бы к его инициализации.
Статические блоки инициализации и инициализация статических переменных класса происходят в порядке их появления в объявлении класса.
Пример 1. Суперклассы инициализируются до подклассов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class Super { static { System.out.print("Super "); } } class One { static { System.out.print("One "); } } class Two extends Super { static { System.out.print("Two "); } } class Test { public static void main(String[] args) { One o = null; Two t = new Two(); System.out.println((Object)o == (Object)t); } } |
Выведет в консоль:
1 |
Super Two false |
Пример 2. Инициализируется только класс, объявляющий статическое поле, к которому было обращение:
1 2 3 4 5 6 7 8 9 10 11 |
class Super { static int taxi = 1729; } class Sub extends Super { static { System.out.print("Sub "); } } class Test { public static void main(String[] args) { System.out.println(Sub.taxi); } } |
Вывод в консоль будет таким:
1 |
1729 |
Пример 3. Инициализация интерфейса не приводит к инициализации суперинтерфейсов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
interface I { int i = 1, ii = Test.out("ii", 2); } interface J extends I { int j = Test.out("j", 3), jj = Test.out("jj", 4); } interface K extends J { int k = Test.out("k", 5); } class Test { public static void main(String[] args) { System.out.println(J.i); System.out.println(K.j); } static int out(String s, int i) { System.out.println(s + "=" + i); return i; } } |
Вывод в консоль будет следующим:
1 2 3 4 |
1 j=3 jj=4 3 |
Обращение J.i — это обращение к константе, поэтому оно не приводит к инициализации I.
Обращение K.j — это обращение к полю, которое объявлено в интерфейсе J, и которое не является константой, поэтому оно приводит к инициализации полей интерфейса J, но не приводит к инициализации суперинтерфейса I, ни к инициализации K.
Не смотря на то что K используется для обращения к j, интерфейс K не инициализуется.
Источник:
https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.4