题主可以通过编译后的class文件反编译来帮助理解初始化过程。
在命令行中的指令javap -l -c -p -v App,执行后就会得到反编译后的内容,下面结合题主给的源码简单分析一下:
下面展示和初始化有关的部分反编译内容
App的class文件
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 "":()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."":()V **调用Object的构造函数**
4: aload_0
5: new #32 // class com/real/test/SubClass
8: dup
9: invokespecial #34 // Method com/real/test/SubClass."":()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文件
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."":()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文件
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."":()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入口方法,打印出“Hello”
上面的过程就是大致的执行顺序。
回答的内容可能会有点混乱,题主看不懂的地方可以问我。