このような質問に遭遇したことのある学生は多いと思いますが、一度確認しただけで忘れてしまい、再度質問されても正しく答えることができません。次に、4 つのステップを通じて、このコードの実行シーケンスを分解し、ルールをまとめます。
最初の質問は、コードの実行順序を調べることです。
public class Parent { static { System.out.println("Parent static initial block"); } { System.out.println("Parent initial block"); } public Parent() { System.out.println("Parent constructor block"); } } public class Child extends Parent { static { System.out.println("Child static initial block"); } { System.out.println("Child initial block"); } private Hobby hobby = new Hobby(); public Child() { System.out.println("Child constructor block"); } } public class Hobby { static{ System.out.println("Hobby static initial block"); } public Hobby() { System.out.println("hobby constructor block"); } }
new Child() が実行されると、上記のコードは何を出力しますか?
このような問題に遭遇したことのある学生は多いと思いますが、一度確認しただけで忘れてしまい、再び問題に遭遇しても正しく答えられないことがあります。次に、クラスの代表者が、このコードの実行シーケンスを分解し、ルールを要約するための 4 つのステップを説明します。
次の 2 つのコードは、コンパイル前とコンパイル後の変更を比較します。
コンパイル前の Child.java
public class Child extends Parent { static { System.out.println("Child static initial block"); } { System.out.println("Child initial block"); } private Hobby hobby = new Hobby(); public Child() { System.out.println("Child constructor block"); } }
コンパイル後の Child.class
public class Child extends Parent { private Hobby hobby; public Child() { System.out.println("Child initial block"); this.hobby = new Hobby(); System.out.println("Child constructor block"); } static { System.out.println("Child static initial block"); } }
渡された 比較から、コンパイラが初期化ブロックとインスタンス フィールドの代入操作をコンストラクター コードの前に移動し、関連するコードの順序を保持していることがわかります。実際、複数のコンストラクターがある場合、初期化コードがコピーされて移動されます。
これに基づいて、最初の優先順位を導き出すことができます:
クラスの読み込みプロセスは、大きく 3 つの段階に分けることができます: 読み込み -> リンク -> 初期化
初期化段階は 8 つの状況によってトリガーされます周志明》P359 "8クラスの初期化をトリガーするタイプ 状況 ") トリガー:
new キーワードを使用してオブジェクトをインスタンス化する場合
静的フィールドを読み取るか設定しますa type (constant" )
型の静的メソッドの呼び出し
リフレクションを使用してクラスを呼び出す場合
クラスを初期化するときに、親クラスが初期化されていないことが判明した場合、親クラスの初期化が最初にトリガーされます。
親クラスの静的コード>サブクラスの静的コード
3 .static コードは 1 回だけ実行されます。protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // 首先检查该类是否被加载过 // 如果加载过,直接返回该类 Class<?> c = findLoadedClass(name); if (c == null) { try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // 如果父类抛出ClassNotFoundException // 说明父类无法完成加载请求 } if (c == null) { // 如果父类无法加载,转由子类加载 c = findClass(name); } } if (resolve) { resolveClass(c); } return c; }コメントを付ければ誰でも簡単に理解できると思います。 親によって委任されたコードから、同じクラス ローダーでは、クラスは 1 回しかロードできず、初期化も 1 回のみに制限されていることがわかります。したがって、クラス内の静的コード (静的メソッドを除く) は、クラスが初期化されるときに 1 回だけ実行されます4. 7e51f00a783d7eb8f68358439dee7daf および 583d030be372af71281df966e84181a5前述したように、コンパイラは、クラス コンストラクター: 583d030be372af71281df966e84181a5 メソッドを自動的に生成します。このメソッドは、すべての静的に変更されたクラス変数の代入アクションと静的ステートメント ブロック (static{} ブロック) を収集し、コードの出現順序を保持します。クラスは初期化されます これに応じて、コンパイラはインスタンス フィールドの代入アクション、初期化ステートメント ブロック ({} ブロック) のコード、およびコンストラクターを収集する 7e51f00a783d7eb8f68358439dee7daf メソッドも生成します。 (コンストラクター)、コードの出現順序を保持します。新しい命令の後に実行されますしたがって、クラスを新規作成するときに、JVM がクラスをロードしていない場合、クラスは初期化されます。最初にインスタンス化されます。 この時点で、3 番目に優先順位の高いルールを作成する準備が整いました:
前の 3 つのルールを結合して、次の 2 つを要約します:
1. 静的コード (静的{} ブロック、静的フィールド割り当てステートメント) > 初期化コード ({} ブロック、インスタンス フィールド割り当てステートメント) >コンストラクター コード
2. 親クラスの静的コード>サブクラスの静的コード
前の概要によると、初期化コードとコンストラクター コードはコンパイラー
親クラス583d030be372af71281df966e84181a5
> サブクラス583d030be372af71281df966e84181a5
> ; 親クラス 7e51f00a783d7eb8f68358439dee7daf
> サブクラス 7e51f00a783d7eb8f68358439dee7daf
冒頭の質問に対応するので、練習してみましょう:
new Child() を実行するとき、new キーワードは Child クラスの初期化をトリガーします。JVM は親クラスがあることを検出すると、まず Parent クラスを初期化し、Parent クラスの 583d030be372af71281df966e84181a5 メソッドの実行を開始します。次に、Child クラスの 583d030be372af71281df966e84181a5 メソッドを実行します (583d030be372af71281df966e84181a5 で何が収集されるか覚えていますか?)。
次に、Child クラスのオブジェクトのインスタンス化を開始します。この時点で、Child の 7e51f00a783d7eb8f68358439dee7daf メソッドを実行する準備ができています。それには親クラスがあることがわかります。7e51f00a783d7eb8f68358439dee7daf メソッドを実行します最初に親クラスの ; メソッドを実行し、次に 7e51f00a783d7eb8f68358439dee7daf の子クラスを実行します (7e51f00a783d7eb8f68358439dee7daf で収集される内容を覚えていますか?)。
これを読めば、最初の質問に対する答えはすでに得ていると思います。最初に出力シーケンスを手書きしてから、コードを書いて自分で検証するのもよいでしょう。
静的は日常の開発で頻繁に使用されます。私が書くたびに、常に 2 つの疑問が頭の中にあります。なぜ静的を使用する必要があるのか? 静的でなくても大丈夫ですか?
この記事からわかるように、静的アプリケーションは単なるクラス変数や静的メソッドをはるかに超えています。古典的なシングルトン パターンでは、static のさまざまな使い方が見られますが、次の記事では、シングルトン パターンを派手に記述する方法について書きます。
以上がJavaのstaticキーワードを一気に理解しようの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。