初期化
クラスの初期化は、クラスの読み込みの最後のステップです。準備フェーズでは、クラスの変数はシステムによって指定された初期値であり、初期化フェーズのプログラムに従って実行されます。
より直接的な表現:初期化フェーズは、クラスコンストラクター<clinit>()メソッドプロセスを実行することです。
<clinit>()メソッドの生成:
クラス内のすべてのクラス変数と静的ステートメントブロック(static {}ブロック)内のステートメントの割り当てアクションを自動的に収集するコンパイラーによって生成されます。コンパイラの収集順序は、ステートメントがソースファイルに表示される順序によって決まります。静的ステートメントブロックは、静的ステートメントブロックの前に定義された変数にのみアクセスでき、後に定義された変数は、前の静的ステートメントブロックに割り当てることができます。ただし、アクセスできません。
public class Test{
static{
i = 0; //变量赋值可以正常编译通过
System.out.print(i);//编译器会提示 “非法向前引用”
}
static int i = 1;// 定义在静态语句快之后的变量
}
<clinit>()メソッドはクラス構築メソッド(<init>())とは異なります。親クラスのコンストラクターを明示的に呼び出す必要はありません。Java仮想マシンは、親がクラスの<clinit>()が実行されました。したがって、Java仮想マシンで実行される最初の<clinit>()メソッドのタイプはjava.lang.Objectである必要があります。
親クラスの<clinit>()メソッドが最初に実行されるため、親クラスで定義された静的ステートメントブロックは、子クラスの変数割り当て操作よりも優れています。
例えば:
static class Parent{
public static int A = 1;
static{
A = 2;
}
}
static class Sub extends Parent{
public static int B = A;
}
public static void main(String[] args){
System.out.print(Sub.B);
}
输出的为2不为1
- <clinit>()メソッドは、クラスとインターフェイスには必要ありません。クラスに静的ステートメントブロックがなく、変数割り当て操作がない場合、コンパイラはこのクラスの<clinit>()メソッドを生成する必要はありません。
- 静的コードブロックはインターフェイスで使用できず、変数割り当て操作は引き続き存在します。インターフェイス実行<clinit>()メソッドは、親インターフェイスで定義された変数が使用される場合にのみ、親インターフェイス<clinit>()メソッドを最初に実行する必要はありません。親インターフェースの初期化の場合、インターフェースの実装クラスは、初期化時にインターフェースの<clinit>()メソッドを実行しません。
- Java仮想マシンは、クラス<clinit>()メソッドがマルチスレッド環境で正しくロックおよび同期されていることを確認する必要があります。複数のスレッドがクラスを初期化すると、他のスレッドがブロッキングキューに入り、プロセスのブロッキングが発生する可能性があります。
注:他のスレッドはブロックされますが、<clinit>()メソッドを実行するスレッドがこのメソッドを終了すると、他のスレッドはウェイクアップ後にメソッドに入りません。同じクラスローダーの下で、1つのタイプのみ一度初期化されます。