Кто вызывает метод инициализатора класса <clinit> и когда?

Я знаю, что в шаблоне байт-кода новый, dup, invokespecial и astore будет вызываться метод инициализации экземпляра <init>, когда кто-то экземпляр класса Java с точки зрения языка Java, но я никогда не понимаю, кто вызывает специальный метод <clinit>, и когда это происходит?

Я предполагаю, что <clinit> вызывается методом до <init>. Может ли какой-нибудь орган дать мне некоторую информацию, чтобы доказать это? Является ли это документировано в спецификации JVM или спецификации языка Java?

Ответ 1

JVM spec §2.9. Специальные методы:

Класс или интерфейс имеет не более одного метода инициализации класса или интерфейса и инициализируется (§5.5), вызывая этот метод. Метод инициализации класса или интерфейса имеет специальное имя <clinit>, не принимает аргументов и недействителен (§4.3.3).

Имя <clinit> предоставляется компилятором. Поскольку имя не является допустимым идентификатором, его нельзя использовать непосредственно в программе, написанной на языке программирования Java. Методы инициализации класса и интерфейса вызываются неявно с помощью виртуальной машины Java; они никогда не вызывается непосредственно из любой инструкции Java Virtual Machine, а вызываются только косвенно как часть процесса инициализации класса.

Подробнее о процессе инициализации класса см. Глава 5.

Ответ 2

<clinit> - статический метод, добавленный javac и вызываемый JVM после загрузки класса. Мы можем видеть этот метод в байт-коде класса с инструментами обхода байт-кода. Обратите внимание, что <clinit> добавляется только в том случае, если классу требуется статическая инициализация, например

public class Test1 {
    static int x  = 1; 

    public static void main(String[] args) throws Exception {
    }
}

public class Test2 {
    static final int x  = 1; 

    public static void main(String[] args) throws Exception {
    }
}

Test1 имеет <clinit>, потому что его поле x необходимо инициализировать с помощью 1; в то время как Test2 не имеет метода <clinit>, потому что его x является константой.

Также интересно отметить, что Class.forName имеет параметр boolen intialize, который определяет, должен ли класс инициализироваться после загрузки или нет.

Ответ 3

<clinit> - это статические блоки инициализации для класса, а также инициализация статического поля и его вызывается JVM.

Java Spec говорит, http://java.sun.com/docs/books/jvms/second_edition/html/Overview.doc.html#12174

The initialization method of a class or interface is static and takes no arguments. It has the special name <clinit>. This name is supplied by a compiler. Because the name <clinit> is not a valid identifier, it cannot be used directly in a program written in the Java programming language. Class and interface initialization methods are invoked implicitly by the Java virtual machine