整理:クラス・ロード・プロシージャ-初期化

4450 ワード

クラス初期化フェーズは、クラスロードプロセスの最後のステップです.クラスのロード中に、ロードフェーズでカスタムローダで参加できるほか、残りの動作は完全に仮想マシンによって主導され、制御され、初期化フェーズになってから、クラスで定義されたJavaプログラムコード(またはバイトコード)の実行が本格的に開始されます.
        仮想マシン仕様では、クラスをすぐに初期化する必要があるのは5つのみです.
  • new、getstatic、putstatic、invokestaticの4バイトコード命令に遭遇した場合、クラスが初期化されていない場合は、まず初期化をトリガーする必要があります.すなわち、newキーワードを使用してオブジェクトをインスタンス化する場合、クラスの静的フィールドを読み出したり設定したりした場合(final修飾、コンパイル中に結果を定数プールに入れた静的フィールドを除く)、クラスの静的メソッドを呼び出す場合.
  • java.lang.reflectパケットのメソッドを使用してクラスを反射呼び出した場合、クラスがまだ初期化されていない場合は、まず初期化をトリガーする必要があります.
  • クラスを初期化すると、その親がまだ初期化されていないことがわかります.まず、その親の初期化をトリガーする必要があります.
  • 仮想マシンが起動すると、ユーザは実行するメインクラス(main()メソッドを含むクラス)を指定する必要があり、仮想機会はまずこのメインクラスを初期化する.
  • JDK 1.7の動的言語でサポートされている場合、java.lang.invoke.MethodHandleインスタンスの最終解析結果REF_getStatic、REF_putStatic、REF_invokeStaticのメソッドハンドルで、このメソッドハンドルに対応するクラスが初期化されていない場合は、まず初期化をトリガーする必要があります.

  •         この5つのシーンの動作を1つのクラスをアクティブに参照することと呼ぶ.それ以外のすべての参照クラスは、パッシブ参照と呼ばれる初期化をトリガーしません.
    public class Test {
        static class Sup {
            public static int i = 1;
            static {
                System.out.println("sup init");
            }
        }
    
        static class Sub extends Sup {
            static {
                System.out.println("sub init");
            }
        }
    
        public static void main(String[] args) {
            System.out.println("Test main: " + Sub.i);
        }
    }
                   ,          。    :
      sup init
      Test main: 1

            定数はコンパイルフェーズで呼び出しクラスの定数プールに格納され、本質的に定数を定義するクラスに直接参照されないため、定数を定義するクラスの初期化はトリガーされません.
    public class MyTest {
        static class Sup {
            public final static int I = new Integer(1);
            public final static int J = 2;	
            static {
                System.out.println("sup init");
            }
        }
    
        public static void main(String[] args) {
            System.out.println("Test main: " + Sup.I);
        //  System.out.println("Test main: " + Sup.J);
        }
    }
        :
      sup init
      Test main: 1
            :
      Test main: 2

            準備フェーズでは、クラス変数はシステムによって初期値(タイプ変数のデフォルトゼロ値)が設定され、初期化フェーズではプログラムコードの設定に基づいてクラス変数やその他のリソースが初期化されます.あるいは、初期化フェーズはクラスコンストラクタ()メソッドを実行するプロセスです.
            ()メソッドの実行中にプログラムの動作の特徴と詳細に影響する可能性があります.
  • ()メソッドは、コンパイラがクラス内のすべてのクラス変数付与動作と静的文ブロック(static{...})文とを自動的に収集することによって生成される.収集順序は、文がソースファイルに表示される順序によって決まります.静的文ブロックは、以前に定義された変数にアクセスできます.その後に定義された変数は、値を割り当てることができますが、アクセスできません.
  • public class Test {
        static {
            //            
            i = 0;
            //      :Cannot reference a field before it is defined       
            System.out.println("i: " + i);
            //       
            System.out.println("i: " + Test.i);
        }
        private static int i = 1;
    }
  • ()メソッドは、クラスのコンストラクタ(または、インスタンスコンストラクタ()メソッド)とは異なり、親クラスコンストラクタを呼び出す必要はありません.仮想機会は、子クラスの()メソッドが実行される前に、親クラスの()メソッドが実行されることを保証します.JAva.lang.Objectは、仮想マシンで最初に()メソッドを実行するクラスです.親の()メソッドが先に実行されると、親定義の静的文ブロックが子クラスの変数付与操作よりも優先されることを意味します.
  • public class Test {
        static class Sup {	
            public static int i = 1;
            static {
                System.out.println("sup static before: " + i);
                i = 2;
                System.out.println("sup static after: " + i);
            }
        }
    
        static class Sub extends Sup {	
            public static int j = 3;
            static {
                System.out.println("sub static before: " + j);
                j = 4;
                System.out.println("sub static after: " + j);
            }
        }
    
        public static void main(String[] args) {
            System.out.println("Test main sub j: " + Sub.j);
        }
    }
  •     :
      sup static before: 1
      sup static after: 2
      sub static before: 3
      sub static after: 4
      Test main sub j: 4
  • ()メソッドは、クラスまたはインタフェースに必須ではありません.1つのクラスに静的コードブロックも変数への付与操作もなく、コンパイラはクラスに生成しなくてもよい.
  • インタフェースでは静的文ブロックは使用できませんが、変数初期化の付与操作が可能であるため、インタフェースもクラスと同様に()メソッドを生成します.ただし、インタフェースはクラスとは異なり、インタフェースを実行する()メソッドは、親インタフェースで定義された変数が使用されている場合にのみ初期化されます.インタフェースの実装クラスは、初期化時にもインタフェースの()メソッドを実行しません.
  • 仮想機会は、1つのクラスの()メソッドがマルチスレッド環境で正しくロックされ、同期されることを保証します.複数のスレッドが同時に1つのクラスを初期化すると、1つのスレッドだけがこのクラスの()メソッドを実行し、他のスレッドはアクティブなスレッドが()メソッドを実行するまでブロックされ、他のスレッドが起動しても()メソッドは再実行されません.同じクラスローダの下で、1つのタイプは1回だけ初期化されます.

  •         本文の内容の主な出所は《Java仮想機を深く理解する》第2版第7章(7.2類のロードのタイミング、7.35の初期化)と、学習、まとめ、総括に用いる.