学习是最好的投资!
クラス変数 (静的変数) > 静的ブロック > コンストラクター > 静的以外のサブクラスJAVA のすべての静的変更はコンパイル時にスペースを割り当てます。オブジェクトは使用時のみ割り当てられます。つまり、new が使用されるとき、サブクラスが親クラスを継承するときは、明らかに最初に親クラスに割り当てる必要があります。父親がいなくても息子は生まれますよね?
この主題は、コンパイルされた class ファイルを逆コンパイルすることで、初期化プロセスを理解するのに役立ちます。 class文件反编译来帮助理解初始化过程。
class
在命令行中的指令javap -l -c -p -v App,执行后就会得到反编译后的内容,下面结合题主给的源码简单分析一下:下面展示和初始化有关的部分反编译内容App的class文件
javap -l -c -p -v App
App
private static com.real.test.App d; descriptor: Lcom/real/test/App; flags: ACC_PRIVATE, ACC_STATIC private com.real.test.SubClass t; descriptor: Lcom/real/test/SubClass; flags: ACC_PRIVATE static {}; descriptor: ()V flags: ACC_STATIC Code: stack=2, locals=0, args_size=0 0: new #1 // class com/real/test/App 3: dup 4: invokespecial #12 // Method "<init>":()V **调用App的构造函数** 7: putstatic #15 // Field d:Lcom/real/test/App; 10: getstatic #17 // Field java/lang/System.out:Ljava/io/PrintStream; 13: iconst_3 **得到数字常量3** 14: invokevirtual #23 // Method java/io/PrintStream.println:(I)V **打印数字常量3** 17: return LineNumberTable: line 4: 0 line 8: 10 line 3: 17 LocalVariableTable: Start Length Slot Name Signature com.real.test.App(); **构造函数详细内容** descriptor: ()V flags: Code: stack=3, locals=1, args_size=1 0: aload_0 1: invokespecial #31 // Method java/lang/Object."<init>":()V **调用Object的构造函数** 4: aload_0 5: new #32 // class com/real/test/SubClass 8: dup 9: invokespecial #34 // Method com/real/test/SubClass."<init>":()V **调用SubClass的构造函数** 12: putfield #35 // Field t:Lcom/real/test/SubClass; 15: getstatic #17 // Field java/lang/System.out:Ljava/io/PrintStream; 18: iconst_4 **得到数字常量4** 19: invokevirtual #23 // Method java/io/PrintStream.println:(I)V **打印数字常量4** 22: return LineNumberTable: line 11: 0 line 5: 4 line 12: 15 line 13: 22 LocalVariableTable: Start Length Slot Name Signature 0 23 0 this Lcom/real/test/App; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: getstatic #17 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #40 // String Hello **得到字符串Hello** 5: invokevirtual #42 // Method java/io/PrintStream.println:(Ljava/lang/String **打印字符串** 8: return LineNumberTable: line 16: 0 line 17: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 args [Ljava/lang/String;
SubClass的class文件
SubClass
static {}; **静态块** descriptor: ()V flags: ACC_STATIC Code: stack=2, locals=0, args_size=0 0: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream; 3: iconst_1 **得到数字常量1** 4: invokevirtual #14 // Method java/io/PrintStream.println:(I)V **打印数字常量1** 7: return LineNumberTable: line 28: 0 line 26: 7 LocalVariableTable: Start Length Slot Name Signature public com.real.test.SubClass(); **构造函数** descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: aload_0 1: invokespecial #23 // Method com/real/test/SuperClass."<init>":()V **调用SuperClass的构造函数** 4: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream; 7: iconst_2 **得到数字常量2** 8: invokevirtual #14 // Method java/io/PrintStream.println:(I)V **打印数字常量2** 11: return LineNumberTable: line 31: 0 line 32: 4 line 33: 11 LocalVariableTable: Start Length Slot Name Signature 0 12 0 this Lcom/real/test/SubClass;
SuperClass的class文件
SuperClass
com.real.test.SuperClass(); descriptor: ()V flags: Code: stack=2, locals=1, args_size=1 0: aload_0 1: invokespecial #8 // Method java/lang/Object."<init>":()V **Object的构造函数** 4: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream; 7: ldc #16 // String 构造SuperClass **得到字符串构造SuperClass** 9: invokevirtual #18 // Method java/io/PrintStream.println:(Ljava/lang/String;)V **打印字符串** 12: return LineNumberTable: line 21: 0 line 22: 4 line 23: 12 LocalVariableTable: Start Length Slot Name Signature 0 13 0 this Lcom/real/test/SuperClass;
大致解释一下内容(如果题主对JVM指令集感兴趣可以看看JVM规范,里面有很详细的解释)。上面反编译内容中星号部分是我加上的注解。按照代码的执行顺序:1.JVM加载App类,App中的静态变量在这时会初始化,对应App中调用App的构造函数的字节码2.跳到App的构造函数,先初始化父类Object,对应App中调用Object的构造函数3.初始化实例变量SubClass,对应App中调用SubClass的构造函数4.此时要先加载SubClass类,同时初始化静态变量并执行静态块,对应SubClass中静态块5.这时执行SubClass中的代码打印出“1”6.SubClass加载完成后执行构造函数,对应SubClass中构造函数7.SubClass构造函数最开始是执行SuperClass的构造函数,对应SubClass中调用SuperClass的构造函数8.执行SuperClass中构造函数中的代码打印出“得到字符串构造SuperClass”9.SuperClass构造完成return到7中的代码位置,继续执行SubClass剩余的代码10.执行SubClass中构造函数代码打印出“2”11.执行完SubClass构造函数return到3中的代码位置,继续执行App剩余的代码12.执行App中构造函数代码打印出“4”13.执行完App的构造函数return到1中代码位置,继续初始化的静态变量14.执行静态块中的方法,答应出“3”15.进入到main
JVM指令集
JVM
调用App的构造函数
Object
调用Object的构造函数
调用SubClass的构造函数
静态块
构造函数
调用SuperClass的构造函数
return
main
アプリ
リーリー
JVM 命令セット
に対応するオブジェクト
アプリ内のサブクラスのコンストラクターの呼び出し
static ブロック
コンストラクターの呼び出しに相当します。サブクラス内のスーパークラスの
return code> 3 のコード位置に移動し、App の残りのコードの実行を続けます。 code>12. App でコンストラクター コードを実行し、「4」を出力します。13. App のコンストラクターを実行した後、return code> を 1 のコード位置に入力し、静的変数の初期化を続けます。14. 静的ブロック内のメソッドを実行し、「3」を許可します。15. main エントリ メソッドを入力します。 「こんにちは」を印刷してください🎜 🎜上記の処理が大まかな実行シーケンスです。 🎜 🎜回答の内容が少しわかりにくいかもしれませんが、分からないことがあれば質問してください。 🎜
App の残りのコードの実行を続けます。 code>12. App でコンストラクター コードを実行し、「4」を出力します。13. App のコンストラクターを実行した後、return code> を 1 のコード位置に入力し、静的変数の初期化を続けます。14. 静的ブロック内のメソッドを実行し、「3」を許可します。15. main エントリ メソッドを入力します。 「こんにちは」を印刷してください🎜 🎜上記の処理が大まかな実行シーケンスです。 🎜 🎜回答の内容が少しわかりにくいかもしれませんが、分からないことがあれば質問してください。 🎜
return code> を 1 のコード位置に入力し、静的変数の初期化を続けます。14. 静的ブロック内のメソッドを実行し、「3」を許可します。15. main エントリ メソッドを入力します。 「こんにちは」を印刷してください🎜 🎜上記の処理が大まかな実行シーケンスです。 🎜 🎜回答の内容が少しわかりにくいかもしれませんが、分からないことがあれば質問してください。 🎜
クラス変数 (静的変数) > 静的ブロック > コンストラクター > 静的以外のサブクラスJAVA のすべての静的変更はコンパイル時にスペースを割り当てます。オブジェクトは使用時のみ割り当てられます。つまり、new が使用されるとき、サブクラスが親クラスを継承するときは、明らかに最初に親クラスに割り当てる必要があります。父親がいなくても息子は生まれますよね?
この主題は、コンパイルされた
class
ファイルを逆コンパイルすることで、初期化プロセスを理解するのに役立ちます。class
文件反编译来帮助理解初始化过程。在命令行中的指令
javap -l -c -p -v App
,执行后就会得到反编译后的内容,下面结合题主给的源码简单分析一下:下面展示和初始化有关的部分反编译内容
App
的class文件SubClass
的class文件SuperClass
的class文件大致解释一下内容(如果题主对
コマンドラインのJVM指令集
感兴趣可以看看JVM
规范,里面有很详细的解释)。上面反编译内容中星号部分是我加上的注解。按照代码的执行顺序:
1.JVM加载
App
类,App
中的静态变量在这时会初始化,对应App中调用App的构造函数
的字节码2.跳到
App
的构造函数,先初始化父类Object
,对应App中调用Object的构造函数
3.初始化实例变量
SubClass
,对应App中调用SubClass的构造函数
4.此时要先加载
SubClass
类,同时初始化静态变量并执行静态块,对应SubClass中静态块
5.这时执行
SubClass
中的代码打印出“1”6.
SubClass
加载完成后执行构造函数,对应SubClass中构造函数
7.
SubClass
构造函数最开始是执行SuperClass
的构造函数,对应SubClass中调用SuperClass的构造函数
8.执行
SuperClass
中构造函数中的代码打印出“得到字符串构造SuperClass”9.
SuperClass
构造完成return
到7中的代码位置,继续执行SubClass
剩余的代码10.执行
SubClass
中构造函数代码打印出“2”11.执行完
SubClass
构造函数return
到3中的代码位置,继续执行App
剩余的代码12.执行
App
中构造函数代码打印出“4”13.执行完
App
的构造函数return
到1中代码位置,继续初始化的静态变量14.执行静态块中的方法,答应出“3”
15.进入到
main
javap -l -c -p -v App
コマンドは、実行後に逆コンパイルされたコンテンツを取得します。質問者が提供したソース コードに基づいて簡単に分析してみましょう。以下は、初期化
アプリ
のクラス ファイルに関連する逆コンパイルされたコンテンツの一部を示していますリーリー
SubClass
のクラス ファイルリーリー
SuperClass
のクラス ファイル🎜 リーリー 🎜内容を大まかに説明します (対象者がJVM 命令セット
に興味がある場合は、JVM
仕様を読むことができます。これには非常に詳細な説明があります)。上記の逆コンパイルされた内容のアスタリスク部分は、私が追加した注釈です。コードの実行順序に従います。
1. JVM は
App
クラスをロードします。App
内の静的変数は、 で App を呼び出します。コンストラクターのバイトコード2.
App
のコンストラクターにジャンプし、最初に親クラスObject
を初期化して呼び出します。アプリ コンストラクターのに対応するオブジェクト
3. インスタンス変数
SubClass
を初期化します。これは、アプリ内のサブクラスのコンストラクターの呼び出し
4. このとき、最初にSubClass
クラスをロードし、静的変数を初期化し、同時にサブクラスのstatic ブロック
に対応する静的ブロックを実行する必要があります。 5. このとき、
SubClass
Out "1" でコードを実行します。6.
SubClass
がロードされた後、constructor7.
SubClass
の構築 この関数は、SuperClass
のコンストラクターを実行することで開始されます。これは、コンストラクターの呼び出しに相当します。サブクラス内のスーパークラスの
8.
SuperClass
でコンストラクターを実行します。コードは「文字列構築 SuperClass の取得」を出力します。9.
SuperClass
構築は次のとおりです。 completedreturn
7 のコード位置に戻り、SubClass
の実行を続行します。残りのコード
10.
SubClass
のコンストラクター コードを実行して出力します。 out "2"11.
SubClass
コンストラクターの実行を終了しますreturn code> 3 のコード位置に移動し、
App の残りのコードの実行を続けます。 code>
12.
App
でコンストラクター コードを実行し、「4」を出力します。13.
App
のコンストラクターを実行した後、return code> を 1 のコード位置に入力し、静的変数の初期化を続けます。
14. 静的ブロック内のメソッドを実行し、「3」を許可します。15.
main
エントリ メソッドを入力します。 「こんにちは」を印刷してください🎜 🎜上記の処理が大まかな実行シーケンスです。 🎜 🎜回答の内容が少しわかりにくいかもしれませんが、分からないことがあれば質問してください。 🎜