【深入Java仮想マシン】の3:クラス初期化


転載は出典を明記してください.http://blog.csdn.net/ns_code/articale/detail/17845821
    クラス初期化は、クラスローディングプロセスの最後の段階であり、初期化段階になって、クラスのJavaプログラムコードの実行が本格的に開始される.仮想マシン規範は厳密に規定されています.四つの場合だけ、直ちにクラスを初期化しなければなりません.
  • は、new、getstatic、putstatic、invokestaticの4つのバイトコードコマンドに遭遇した場合、クラスはまだ初期化されていない場合、その初期化をトリガする必要があります.この4つの命令を生成する最も一般的なJavaコードシーンは、newキーワードを使ってオブジェクトを実装する場合、クラスの静的フィールド(static)を読み込んだり設定する場合(staticによって修正されたり、finalによって修正されたりして、コンパイル期間中に結果を定数プールの静的フィールドに入れた場合を除く)と、クラスの静的方法を呼び出す場合である.
  • は、クラスをJava.lang.refectパケットの方法で反射呼出しする場合、クラスがまだ初期化されていない場合、その初期化をトリガする必要がある.
  • 当初、一つのクラスが開始されたとき、その親類がまだ初期化されていないことが発見された場合、先に親類の初期化をトリガする必要がある.
  • 仮想マシンが起動すると、ユーザは実行するプライマリクラスを指定する必要があり、仮想機会は先にプライマリクラスを実行する.
  •     仮想マシンは、これらの4つのケースだけがクラスの初期化をトリガすることを規定しています.1つのクラスに対してアクティブな参照といいます.それ以外のすべての引用クラスの方式は、その初期化をトリガしません.受動参照といいます.受け身引用を説明するために、いくつかの例を挙げます.
        1、親クラスの静的フィールドをサブクラスで引用します.この場合、サブクラスの引用は受動的な参照です.したがって、最初の化学種はなく、親タイプのみを初期化します.
    class Father{
    	public static int m = 33;
    	static{
    		System.out.println("      ");
    	}
    }
    
    class Child extends Father{
    	static{
    		System.out.println("      ");
    	}
    }
    
    public class StaticTest{
    	public static void main(String[] args){
    		System.out.println(Child.m);
    	}
    }
        実行後の出力の結果は以下の通りです.
        親が初期化されました    33
        静的フィールドでは、このフィールドを直接定義するクラスだけが初期化されるので、そのサブクラスを通じて親クラスで定義された静的フィールドを参照すると、親クラスの初期化をトリガするだけで、サブクラスの初期化をトリガしない.
        2、定数はコンパイル段階で呼び出されるクラスの定数プールに保存されます.本質的にはこの定数を定義するクラスに直接引用されていませんので、定数を定義するクラスの初期化はトリガされません.
    class Const{
    	public static final String NAME = "    ";
    	static{
    		System.out.println("   Const ");
    	}
    }
    
    public class FinalTest{
    	public static void main(String[] args){
    		System.out.println(Const.NAME);
    	}
    }
        実行後の出力の結果は以下の通りです.
        私は常量です
        プログラムではconstクラスの定数NAMEを引用していますが、コンパイル段階ではこの定数の値「私は定数」を呼び出したクラスFinalTestの定数プールに格納しています.定数Conts.NAMEへの参照は実際にFinalTest類の自己定数プールへの参照になります.つまり、実際にはFinalTestのクラスファイルの中にはConst類の記号引用入口がありません.この二つの種類はClassファイルにコンパイルされてからは何の連絡もありません.
        3、配列定義によってクラスを参照すると、トリガクラスの初期化ができません.
    class Const{
    	static{
    		System.out.println("   Const ");
    	}
    }
    
    public class ArrayTest{
    	public static void main(String[] args){
    		Const[] con = new Const[5];
    	}
    }
       
    実行後は何の情報も出力されません.Costクラスは初期化されていないということです.
        このコードの中で、もう一つの「LLConst」というクラスの初期化がトリガされました.これは仮想マシンによって自動的に生成され、java.lang.Objectのサブクラスに直接継承され、バイトコードコマンドnewarrayによって開始された動作です.これは配列参照タイプの初期化です.それを初期化していません.私たちがコン配列における各Cost類元素の実装コードを加えると、Cost類の初期化がトリガされます.
    class Const{
    	static{
    		System.out.println("   Const ");
    	}
    }
    
    public class ArrayTest{
    	public static void main(String[] args){
    		Const[] con = new Const[5];
    		for(Const a:con)
    			a = new Const();
    	}
    }
        これで次のような出力結果が得られます.
        初期化Costクラス    四条規則の第一条により、ここのnewはConst類をトリガします.
        最後にインターフェースの初期化プロセスとクラス初期化プロセスの違いを見てください.
        インターフェースも初期化プロセスがあります.上のコードは静的な文ブロックで初期化情報を出力します.インターフェースでは「static{}」文ブロックは使えませんが、コンパイラはインターフェースのために「clinit」類コンストラクタを生成して、インターフェースで定義されたメンバー変数を初期化します.
       二つのものは初期化時の主な違いは、一つのクラスが初期化された時に、その親クラスは全部初期化されたが、一つのインターフェースが初期化された時に、その親インターフェースが全部初期化されたとは要求されません.本当の親インターフェースに使用された時にのみ、親インターフェースが初期化されます.この点もクラス初期化の場合とは違って、第2の例を振り返ってみると、クラスのstatic final定数を呼び出したときに、クラスの初期化をトリガしないことが分かりますが、インターフェースのstatic final定数を呼び出したときに、このインターフェースの初期化をトリガします.