クラスローディング機構のJava Virtual Machine:JVMノート

序文

Java型を形成し、メモリ内にクラスファイルからロードされたクラスを記述し、データを確認し、初期化を解析し変換する仮想マシンのデータは、仮想マシンのクラスローディング機構である仮想マシンとして使用することができます。

  • クラスのロードプロセス

    クラスが仮想マシンのメモリにロードされ始めて、そのライフサイクル全体をアンロードするためのメモリ位置は、前記ローディング、検証、製造、解像度、初期化、およびアンロードの使用であって、前記検証、製造、三つの部分集合的に接続された解析を。図1-1に示す7段階の発生のこの順序。
    図1-1:フローチャートクラスローダ

ロード、検証、準備、初期化シーケンスおよびこれらの5つのステージをアンロードし、上の図は、クラスのロード処理がこの順序に従って段階的に開始しなければならない固定されているが、ステージは必ずしも解決しません。彼ができるいかなる場合にも、その後、Java言語ランタイムバインディングをサポートすることです初期化フェーズ、後に再起動します。ステージ上にある同僚は、一般的に別の混合は、通常、位相の実行、活性化の別の相(例えば、クラス内の別のクラスを初期化する)中に呼び出されるものと交差しています。

  • クラスのロード時間

    負荷:どのような状況下では、クラスのロード・プロセスの最初のフェーズを開始する必要がありますか?Java仮想マシンの仕様では、この点に特定の仮想マシンへの無料把握し、義務を実行しません。しかし、初期段階のため、仮想マシンの仕様は、厳格なルールであり、唯一の5クラスの場合は、直ちに(ロードは、検証、準備は当然その前に開始する必要があります)初期化する必要があります。

    • 遭遇したnew 、getstatic、putstatic、invokestaticクラスは、国家予約石油化学を行わない場合は、これらの4つのバイトコード命令の間に、あなたはその初期化をトリガーする必要があります。このシナリオでは、4つの命令を生成している:使用newキーワードは、オブジェクトをインスタンス読み取り、または唯一のクラス静的変数時刻、及び通話(静的フィールドを除いては、最終変形例であり、時定数プールに結果をコンパイルされている)しますときに、クラスの静的メソッド。
    • 使用しjava.lang.reflectたパッケージのアプローチと呼ばれるクラスを反映するとき、クラスが初期化されていない場合、あなたは彼らの初期化をトリガーする必要があります
    • クラスを初期化する場合、親クラスが初期化されていない場合、あなたは親クラスの初期化をトリガーする必要があります。
    • 仮想マシンが起動すると、ユーザーが実行されるマスタークラスを開発する必要があります(クラスのmain()メソッドが含まれている)、仮想マシンは、このマスタークラスを初期化します。
    • 場合動的言語サポートJDK1.7を使用する場合java.lang.invoke.MethodHandle、分析の最終結果のインスタンスREF_getStatic、REF_putStatic、REF_invokeStaticハンドルの方法、及びクラスメソッドに対応し、このハンドルが初期化されていない、その初期化をトリガーする必要があります。

    クラスのシーンが初期化されたトリガ以上の5種類については、仮想マシンの仕様は今のところ予定さ非常に強い使用しています:有且只有クラスアクティブな参照であることが知られている行動のこれらの5つのシーンを、それに加えて、クラスメソッドへのすべての参照は、次の例のように、受動的基準として知られている、初期化をトリガしていません。

public class Parent {
    public static int a = 1;
    static {
        System.out.println("Parent init");
    }
}
public class Son extends Parent{
    static {
        System.out.println("Son init");
    }
}
   public static void main(String[] args) {
        System.out.println("args = [" + Son.a + "]");
    }
输出结果:
Parent init
args = [1]
复制代码

静的フィールドの場合、唯一のこのフィールドのクラスを定義する指示初期化され、したがって、親クラスで定義された静的フィールドは、それだけにとして、サブクラスを初期化するトリガすることなく、親クラスの初期化をトリガーするそのサブクラスで参照するか否かトリガーロードと検証サブクラスは、できる、これは、SunのHotSpot仮想マシンのために、特定の仮想マシンに依存し、仮想マシンの仕様を指定していない-XX:_TraceClassLoading操作は、パラメータのサブクラスをロードするためにつながる観察すること。

また、配列定義によってクラスを参照するために、それは初期化のこのタイプをトリガしません。

   public static void main(String[] args) {
        Parent[] parentArry = new Parent[10];
    }
复制代码

上記のコードを実行した後に何も出力、ノートがトリガされませんでしたしませんParent初期化フェーズクラスを。しかし、別のグループをトリガし、このコードが呼び出され[Lxxx.xxx.Parent(前面的xxx指代类的包名)、クラスの初期化が、これは知っているバイトコードの記事の前に、ビット見慣れていない[Lオブジェクトの配列で表され、ここで。それは、自動的に仮想マシン、および直接命令は、バイトコードの操作によって作成されたオブジェクトのクラス継承によって生成されるnewarrayトリガ。このクラスは、要素タイプが表すParent一次元アレイ、アレイは、プロパティとメソッド(メソッドは、ユーザとクローンの長さだけ直接呼び出すことができる)このクラスで実装さを有するべきです。投げるために検査時間配列境界のJava言語でArrayIndexOutOfBoundsException例外をするが、アレイ型パッケージの異常検出要素がアクセスされないが、配列アクセスカプセル化されたxaload、xastoreバイトコード命令。

あなたは、定数の静的最終修正のクラスを参照すると、このような初期化をトリガしていないとき

public class Parent {
    public static final int a = 1;
    static {
        System.out.println("Parent init");
    }
}
  public static void main(String[] args) {
        System.out.println("args = [" + Son.a + "]");
    }
输出结果:
args = [1]
复制代码

以来final修飾不変定数の値は、それは、コンパイル時定数伝播最適化で一定プールのメインクラス(クラスのメインメソッド)に定数1に格納された値を渡し、これのメインカテゴリの後であろう実際の一定の基準1が、であり、実際には、クラスドキュメントのメインクラスとしなかったメインクラスの定数プール自体への参照に変換され、Parent2つの異常クラスの後に一連の任意のクラスが存在しない場合、シンボリック参照れたよう連絡。

クラスローディングプロセスとシェルフインタフェースああプロセスは、インタフェースのため若干異なっているいくつかの特別な命令実行する必要があります。インターフェイスは、初期化プロセスがあり、このポイントとクラスを同じですが、インターフェースが使用できないstatic{}文のブロックを、コンパイラは、まだインタフェースを生成します<client>クラスコンストラクタは、インターフェイスで定義されたメンバ変数を初期化します。インタフェースとクラスは、先に説明した定期的な差別化されている第三のシナリオを初期化する必要があります。クラスの初期化を、既に初期化され、すべての親クラスが必要です。すべての負の初期化が完了するためではなく、初期化時に1つのインタフェースは、初期化されます負(このような参照としてインターフェイスで定義されている定数、)への言い訳を使用してのみ、リアルタイムで、言い訳を必要としません。

  • ステップクラスロード

この5相ロック実行を初期化するために詳細にクラスローディング、すなわちローディング、検証、製造、解像度、特定の動作の全体処理を説明します。

  • ロード

ロード・フェーズでのステージのクラスローディングプロセスをロードし、仮想マシンを、約3つのことを完了しています

  • 1クラスの完全修飾名によって定義されるようなバイナリバイトストリームを得ることができます。
  • 2.バイトストリームは、ランタイムデータ構造領域法に静的記憶構造を表します。
  • メモリ3.各種のデータを持っているこのクラスのアクセス・エントリ領域の方法として、このクラスのクラスオブジェクトの代表を生成します。

クラス負荷の他の段階のために、無負荷相のアレイは、ローディング相系を提供するように使用することができる、最も制御現像剤である(正確な、ローディング・フェーズの動作はバイナリバイトストリームのクラスで取得されるべき)完了するブートクラスローダー、ユーザ定義(例えば、バイト暗号、復号化されたクラスをロードするために、カスタムクラスローダ)にクラスローダによって実行することができる、開発者は、によって独自のクラスを定義することができバイトストリームを制御するためのローダを取得します。

しかし、配列クラスが直接作成したJava仮想マシンであるクラスローダによって作成されていません。結局状配列要素のタイプ、作成するクラスローダ、次の規則クラスのアレイを作成するプロセスをテストするためただし、データ・タイプとクラスローダは、依然として、非常に緊密な関係を有します。

  • 1.参照型配列型場合は、その後のクラスローダにあるコンポーネントタイプのクラスローダの識別されたクラスの名前空間をロードする、成分のこのタイプをロードする必要が教えてくれる。
  • 2.配列型の基礎となるデータ型、Java仮想マシンと連想配列は、ブートストラップクラスローダとしてマークされている場合。
  • 3.可視性配列クラスの可視性は、コンポーネントタイプは、参照型でない場合は、そのアレイの視認性が公衆にデフォルト設定されます、そのコンポーネントのタイプと一致しています。

ローディング段階が完了した後、仮想マシン自体によって定義されたメソッド領域の処理領域データ格納形式で格納された仮想マシンの外部バイナリバイトストリームフォーマットに従って仮想マシンは、メモリクラスにクラスをインスタンス化しますオブジェクト(と明示されていないのHotSpot仮想マシンのJavaヒープ内mClassの面で彼がターゲットだったが、むしろ特殊なオブジェクトが、この方法は、ゾーン内に格納されている)、このオブジェクトは、領域内のこれらのデータ型にアクセスするための方法として機能します外部インタフェース。

パートのロード・フェーズとその後のクロス接続フェーズは、ロード・フェーズが完了していないとき、接続フェーズが始まっているが、実施されるが、操作のロード・フェーズに巻き込まれたものは、まだ接続フェーズです。

  • 検証

    検証は、このステップの目的の第一段階に接続されている現在の仮想マシンのバイトストリームクラスファイルの要件に沿った情報が含まれており、仮想マシン自体の安全性を危うくしないようにすることです。仮想マシンが入ってくるバイトストリーム、その完全な信頼をチェックしない場合は有害と原因システムを搭載したバイトストリームがクラッシュするので、それであってもよいので、自分の保護は重要な仕事であるため検証は仮想マシンです。ファイル形式の検証、メタデータの検証、バイト:全体的に観点から、「Java仮想マシン仕様(JSEバージョン7)」の2011年のリリースから、開発段階では、主に検証操作を次の4つの段階を完了しますコードチェック、シンボリック参照の検証。

  • 1ファイルフォーマット検証バイトストリームクラス準拠のファイルフォーマットかどうかを確認するには、仮想マシンプロセスの現在のバージョンであることができる、そのような検証は、以下の点を含んでいてもよいです。

    • マジックナンバー0xCAFEBABYの始まりかどうか。
    • 現在の仮想マシンの処理範囲でメジャーとマイナーバージョン番号。
    • 定数プールがサポートされていない定数型(タグのチェックマーク定数)があります。
    • これは、定数が存在しないか、定数のタイプを満たしていない指し示す指標値の一定の様々なを指します。
    • 定数のUTF8でエンコードされたデータCONSTANT_Utf8_infoタイプの不遵守するかどうか。
    • 自身のさまざまな部分でのクラスファイルや書類その他の追加情報が削除される場合、または。......

上記検証ポイントのほんの一部であり、オブジェクトは、パケットフォーマットは、Javaデータ型を正しく受信バイト・ストリームの要件に準拠し解決することができます。3つの検証ステージは、すべてのストレージ構造を得る方法に基づいています後ろだけイエンチョウのこの段階を通じて、バイトストリームは、メモリ記憶領域の方法を入力します、もはや直接バイトストリームを操作していません。

  • 2メタデータの検証第2のステップは、Java言語仕様で説明した要件を満たして、その情報を保証するために、バイトコード情報セマンティック分析を記載されている、検証点は、この段階で含まれ、以下の通りであります:

    • クラスは親クラスを持っているかどうか(オブジェクトを除く、すべてのクラスは、親クラスを持っている必要があります)。
    • かどうかは、このクラスの親を継承したクラス(最終修正クラス)を継承することはできません。
    • クラスは、その親クラスまたはインタフェースの実装要件のすべてのメソッドを実装するかどうかを、抽象クラスではない場合。
    • クラスフィールド、メソッド、および親が矛盾である場合(例えば、親クラスの最後のフィールドをカバーします)。......
  • 3.バイトコード検証検証プロセスは、最も複雑な段階であり、主な目的は、プログラムのセマンティクスを決定するために、データ・フローと制御フロー解析論理、正当なものです。検査メタデータの後、この段階は、次のようなイベントを、実行中の仮想マシンのセキュリティに害をしない方法の検証クラスを確保するために、クラスのメソッド本体の分析を検証します。

    • オペランドスタックと命令コード列のデータ・タイプは、例えば、Shique長いをロード、オペランドスタックint型データに格納されません、一緒に働くことができます。
    • 保証ジャンプ命令(GOTO)は、メソッド本体以外のバイトコード命令にジャンプしません。
    • 有効な変換型ボディを確保する方法。......

    ボディは小切手でのクラスメソッドでない場合、それは確かに問題ですが、必ずしもそうではない、チェックスルーが完全に安全である、それはあります通过程序去校验程序逻辑是无法做到绝对准确的

    JDK1.6のあるJavac、コードproperty属性テーブルによる方法が増えると呼ばれた後、最適化された仮想マシン、バイトコード検証段階で時間の過剰消費を避けるために、仮想マシンの設計チームStackMapTable属性は、属性が先頭にすべての基本的な損失のAHローカル変数とオペランドスタックテーブル適正な動作の正当性を説明し、バイトコード検証の際に、これらの状態は、メソッド本体に係るプログラムを導出する必要はありません。あなたはチェックする必要がありStackMapTable適法性を帰するレコードを、および検証済みバイトコードは、いくつかの時間を節約する推論の型チェックを、型に変換されます。

  • シンボリック参照の4検証

    最後のステージは、直接基準時間にシンボル参照をチェックする仮想マシンで発生し、第三の段階で発生する変換動作を解析に接続され、検証は各定数プールに(独自のクラス以外としてシンボリック参照を見ることができます型文字参照)の情報整合性チェックは、また次のことをチェックする必要があります。

    • 完全修飾名の文字列によって符号は、対応するクラスで説明見出すことができるかどうか。
    • 一致方式記述子ワードと説明したメソッドの名前があるかどうかは簡単で、指定したクラスのフィールドです。
    • アクセスタイプシンボリック参照クラス、フィールド、メソッドは、アクセスの現在のタイプかどうかであってもよいです。目的は、......シンボルが検証が分析動作を適切に行うことができることを保証することである参照する。符号によって参照されない場合、それがスローされIncompatibleClassChangeError、例えば、異常なサブクラスNoSuchField(Method)Error

    あなたのコードは、その後、実施段階で使用して検討することもでき、繰り返し使用して検証されている場合は、仮想マシンの場合は、検証フェーズは重要ではなく、本質的なステージは、ある-Xverify:noneにクラス検証措置のほとんどをシャットダウンするパラメータを時のクラスのロードを短縮します。

  • レディ

    準備フェーズは、公式には、これらの変数は、プロセスに割り当てられたメモリ領域で使用され、初期位相値を設定するクラス変数およびクラス変数にメモリを割り当てることです。このステージは二つの概念を強調する必要が混同するのは簡単です:まず、メモリ割り当てのために、この時間は唯一のクラス変数(静的変数)を含み、そしてオブジェクトがインスタンス化されるとき、インスタンス変数がオブジェクトと一緒に割り当てられるインスタンス変数が含まれていません。 Javaヒープに、第二に、ここでは、フィールドの初期値は、彼のゼロ値のデータ型のことです。

  public static  int number= 1;
  public static final int numberFinal= 123;
复制代码

上記の例ではnumber、この時間がまだ開始されていないため、準備フェーズ0の代わりに1、後の初期値とJavaメソッド、およびnumber1の値putstaticプログラムは、コンパイルされたクラスのコンストラクタに格納された後の命令<clinit>()ので、方法のうちnumberアクション1の割り当ては、初期化段階中に実行されるであろう。

しかし、例外的な状況での存在下でのフィールド属性テーブルクラスフィールドもしConstantValueプロパティ(最終変更)、及び準備フェーズ変数にそのnumberFinal指定された値に初期化されます。javacはする時間をコンパイルしますnumberFinal生成するConstantValue仮想マシンに基づいて説明する準備段階で、プロパティをConstantValue123に値を設定します。

  • 解決

    段階の解析は直接参照を置き換えるプロセスへのシンボリック参照の定数プール、で参照シンボルに仮想マシンで、Java仮想マシンの定数プールは:JVMノートを何度も述べたように、彼はクラスファイルでCONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info、定数の他の種類を表示されます直接参照と記号参照などの分析フェーズ、どのような関連性はありますか?

    符号(SymbolicReferences) 参照に記載されたシンボルの特定のセットへのシンボル参照は、リテラルシンボルは、任意の形態であってもよく、明確であれば使用することができるように、ターゲットに配置することができます。しかし、ゴールは、参照は、我々は多くの場合、直接参照して、それを置き換えるために、後の段階で、このようなコンテンツを指すように必要な未来を表してプレースホルダに似ている、メモリにロードされているとは限らないです。仮想マシンは明らかにクラスファイル形式のJava仮想マシン仕様で定義されたシンボリック参照のないようリテラル形式、記号参照の様々な一貫性がなければなりません受け入れることができます。

    直接参照(SymbolicReferences) 直接参照は、オブジェクトへの直接のポインタであってもよいし、相対オフセットは、ターゲット・プロファイル・ハンドルを標的とすることができます。異なる仮想マシン・インスタンスで翻訳関連文献の仮想マシンの実装のメモリレイアウトを直接参照は、一般的にシンボルへの直接参照と同じではありませんです。あなたがその目標基準への直接参照を持っている場合は、すでにメモリ内に存在している必要があります。

    特定の時間分解能の位相を提供していない仮想マシンの仕様は、要件の実装で発生したanewarray、multianewarray、checkcast、getfield、getstatic、instanceof、invoke(dynamic,interfance,special,static,virtual)、ldc、ldc_w、new、putfield、putstatic彼らは参照を使用するシンボルを解析するために、16バイトのコードの前に。だから、仮想マシンの実装ができるクラスローダを解析し、またはシンボリック参照は、必要に応じてそれを使用する前に、それを解決することですまで待機するシンボリック定数プール参照に上にロードされたときにそれが最終的に判断すること。

    別にinvokedynamic指示より、一次キャッシュの分析結果の仮想マシンの実装は、プールの量は、常に直接ログで参照され、分析動作を繰り返さないように、解像度が成功した場合、定数は、解像度状態として識別される、または参照シンボル失敗し、その解決への以降の参照も成功または不規則通知を受け取る必要があります。

    以下のためにinvokedynamicそれがして、以前に来るときの指示、invokedynamic解決のシンボリック参照を介してトリガ命令、分析は他に結果という意味ではありませんinvokedynamicforceコマンドに均等に。のでinvokedynamic、命令の目的は常に動的言語のサポートに使用されてきた、それは動的起動修飾子と呼ばれる基準点に対応し、ここでの意味ダイナミックなプログラムが行われることができるアクションを解析する際に、この命令を実行するようになるまで待たなければなりません。対照的に、解決トリガすることができ、残りのコマンドはちょうど相をロードで完了することができる、静的であり、それはまだコードの解析の実行を開始するために開始されていません。

    メインクラスまたはインタフェース、フィールド、メソッドのクラスのための分析動作、インターフェースメソッド、タイプ法、及び方法は、シンボリック参照の7点クラスの修飾子を処理するコールは、ここで新しい動的言語サポートJDKと、上記4種類の、最後の三つを紹介します密接に関連し、言うことをここで行うことはあまりないとされ、最初の3つは、定数プールのためのものですCONSTANT_(Class、Fieldref、Methodref、InterfaceMethodref)_info

    1.クラスまたはインタフェースの解析

    クラスと仮定W参照すると、シンボル解析されてはならないNクラスまたはインタフェースに解決Oプロセスを完了するための仮想マシンは、次の3つのステップで構成されていることを、直接参照を。

    • もしOない配列型、完全修飾名を渡します仮想マシンは、クラスをロードするクラスローダにN Wを表していますOメタデータの検証、バイトコード検証が必要とされているため、ロード・プロセスでは、他のクラスをロードするためにアクションをトリガーすることが可能であるロードプロセスが異常があらわれたら、分析プロセスが失敗しています。

    • 場合O、アレイタイプ、およびタイプ(記述子[LXXX / XXX)のオブジェクトの配列は、それが上記の規則に従った配列要素のタイプをロードし、仮定として先行N記述子ロックフォーム場合、それがロードされオブジェクトエレメントタイプは、仮想マシンによって、オブジェクトおよび要素を表すのこの配列の次元のアレイを生成します。

    • 上記の二つのステップ場合は何も異常が中に存在していないc、確認して実際に分析が完了する前に、有効なクラスやインタフェースが、また、シンボリック参照の検証のためになってきている仮想マシンWの可用性Oのアクセス権を持っていないことが判明すれば、アクセスを当局は、スローされますIlleagalAccessError例外を。

    2フィールドの決意

    未解決のシンボル参照を解決するための素敵なフィールドは、テーブルのフィールドで初めてとなるclass_indexのインデックスのエントリCONSTANT_Class_info解析するシンボリック参照、それはクラスのシンボルであり、インタフェースの参照フィールドが属する、それはフィールドを理解するためには、我々は最初のソリューションなければなりません彼らはクラスです。

    • クラス自体が直接参照のフィールドに、その後の直接リターン、目標と一致しているフィールド名とフィールド記述子シンプル含まれている場合の後、クラスを解析されました

    • クラスがインタフェースを実装している場合、インターフェイスが含まれている場合は、単純なフィールド名とフィールド記述子がターゲットと一致している、各インターフェイスの継承と彼の親インターフェースの再帰検索に合わせてだろう、そしてダイレクトリターンは、直接フィールドを参照します。

    • クラスがオブジェクトでない場合は、親クラスがターゲットと一致しているフィールド名とフィールド記述子シンプルが含まれている場合、それは再帰的に、継承に基づいて、親クラスを検索します、その後、直接のリターンは、直接フィールドを参照します。

    • 上記の手順が失敗した場合、その後、スローNoSuchFieldError例外を。

    • あなたが引用返されるフィールドに同じアクセス権を持っていない場合、スローIlleagalAccessError例外を。

    • 同じ名前のフィールドは、インタフェースクラスと親クラス、またはその親クラスで見つかった複数のインターフェースに表示された場合、コンパイラは、可能なコンパイルを拒否します。

    3.解析クラスのメソッド

    クラスメソッドの解釈と同じフィールドを解析するステップが、また、配置されているクラスのメソッドを解析する必要があります。そして、クラスメソッドのためのステップ以降の検索に従ってください。

    • 1つの定数型定義)クラスメソッドテーブルインデックスがインターフェイスで検出された場合(一方Methodref一InterfaceMethodref)メソッドとインターフェースメソッド参照シンボルが分離されている入力、それがスローされIncompatibleClassChangeErrorた例外を。

    • 2)単純なルックアップ続く最初のステップは、名前が含まれていると記述語は、標的クラス、直接処理に戻るへの直接参照して一致した場合。

    • 3)それ以外の場合は、親クラスがターゲットマッチに関連しているルックアップ名とフィールド記述子の単純な再帰が含まれ、その後、直接参照の直接的な方法に戻ります。

    • それは抽象クラスでない場合4)それ以外の場合は、インターフェースおよびその親再帰検索インタフェースのリストを実装するクラスは、(クラスは抽象クラスであることを示す、存在する場合には単純名とフィールド記述子は、ターゲットと一致している含まれていこのクラスは)この方法を見つけるだろう、この時間は、スローAbstractMethodError例外を。

    • 5)上記の手順では、スロー、動作しませんNoSuchMethodError例外を。

    • 6)あなたが同じアクセス方法を持っていない場合にスローへの参照を返しIlleagalAccessError、例外を。

    4.解析しクラスメソッド

    同じ古い、同じインタフェースのメソッドは、インタフェースのメソッドテーブルのうち解析する必要があるclass_info参照を属するインデックスを作成するシンボルクラスまたはインタフェースのメソッドを。その後、手順、後続の検索インターフェイスメソッドに従ってください。

    • 1)それはインターフェースのメソッドテーブルに見つかった場合、コントラスト基づく分析方法インタフェースではなくクラスに対応するインターフェイスが、スローされるIncompatibleClassChangeError例外を。

    • 2)あなたが最初のステップを通過し、そしてそれは、単純な名前が含まれており、フィールド記述子は、インタフェースの目標と一致していることが判明した場合、単純にメソッドへの直接参照を返します。

    • 3)それ以外の場合は、親インターフェースインターフェース再帰検索それは単純名とフィールド記述子がターゲットと一致している含まれている場合は、Objectクラスまで、直接法への直接参照を返す、参照してください。

    • 4)上記の手順では、スロー、動作しませんNoSuchMethodError例外を。

    • 5)のでアクセスがないので、インタフェースメソッドは、デフォルトでは、公開されているので、インターフェイスメソッドがスローされませんIlleagalAccessError例外を。

  • 初期化

    最終ステップクラス初期クラスロード処理、上記クラスローディングプロセスは、ユーザアプリケーションが参加の外部からのクラスローダを定義することができ、負荷相の間を除いて、残りの動作は完全に仮想マシンと制御によって支配されます。初期化フェーズに、実際にクラス定義(またはバイトコード)でJavaコードを実行し始めました。

    準備段階中、変数はシステム要件一度初期値が割り当てられており、初期化フェーズ中に、計画開発された手順に従って、クラス変数やその他のリソースを初期化するために、別の方法を発現:初期化フェーズは、クラスコンストラクタへのポインタである<clinit>()方法プロセス。

    <clinit>()この方法は、ブロック合併に自動コンパイラの電話クラスのすべてのクラス変数(静的変数)代入操作と静的ステートメントのステートメントで、オーダーコンパイラコレクションは、意思決定のソースファイルに文で出現順にありました静的文はそれの後に変数を定義し、あなたの前にステートメントのブロック内で定義された静的変数へのアクセスのみをブロックし、あなたはブロックの前で静的ステートメントを割り当てることができますが、アクセスすることはできません

public class Parent {
    static {
        a=2;
        System.out.println("Parent init"+a);
    }
    public static  int a = 1;
}
复制代码

後で1を再割り当てされ、コードブロック内の以下のクラス変数を呼び出すことはできません、表示されるように、コードブロックが、何の役に割り当てられることがコード上のilleagal forward reference誤りを

<clinit>()インスタンスコンストラクタ施工方法やクラスは、つまり、<init>()異なる、それは親クラスのコンストラクタ、呼び出すためにそれを表示する必要はありませんサブクラスであることを確認するために仮想機会を<clinit>()実行メソッドの前には、親クラスの<clinit>()メソッドは、つまり、父が実装されました静的サブクラス割り当てで定義された変数によるクラスステートメントブロック最初の仮想マシンで実行されるように、<clinit>()メソッドは、オブジェクト確かに基づきます。

以下の実施例2の結果は、静的割り当て親クラスため、最初の実装サブクラスよりも、出力されます

public class Parent {
    public static  int a = 1;
    static {
        a=2;
    }
}
public class Son extends Parent{
      public static int b=a;
}
 public static void main(String[] args) {
        System.out.println("args = [" + Son.b + "]");
    }
复制代码

<clinit>()この方法は、コンパイラーは、クラスに対して生成されないことがあり、クラスが文の静的なブロックではない場合、クラス変数のない割り当てが存在しない、必要ではない<clinit>()方法。

インターフェイスブロック静的ステートメントは使用できませんが、インターフェースとクラスである生成するように、変数の割り当てを初期化まだある<clinit>()方法が、クラスが異なるインターフェースである、心臓魔法インタフェースが<clinit>()親インタフェース実行する必要がない<clinit>()、方法が変数は、親を使用してインターフェイスで定義された場合にのみ、親はまた、インタフェースの実装クラス初期化するとき、インタフェース行われません、インターフェイスを初期化する<clinit>()方法。

クラス確保するための仮想機会<clinit>()の方法は、複数のスレッドが戻ってクラスにこのクラスは、唯一つの実行スレッドを初期化する場合は、なしとマルチスレッド環境での正しいロック、ある<clinit>()方法を、他のスレッドが待ってブロックする必要があり、これは静的シングルトンの実装の原則。

  • 概要

    「綿密なJava仮想マシン」から、この記事では、興味のある友人は、この本の中に見ることができます。

おすすめ

転載: juejin.im/post/5dc0dc986fb9a04a680f5183