学习是最好的投资!
类变量(静态变量)>静态块>构造方法>普通成员变量>子类除静态外。JAVA所有static修饰的都会在编译时分配空间,而对象只会在使用时才分配即new的时候,子类继承父类很明显需要先给父类分配,没有父亲哪来的儿子是吧
题主可以通过编译后的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.初始化实例变量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,对应App中调用SubClass的构造函数4.此时要先加载
JVM指令集
JVM
调用App的构造函数
Object
调用Object的构造函数
调用SubClass的构造函数
静态块
构造函数
调用SuperClass的构造函数
return
main
中的代码打印出“1”6.
构造函数最开始是执行
类变量(静态变量)>静态块>构造方法>普通成员变量>子类除静态外。JAVA所有static修饰的都会在编译时分配空间,而对象只会在使用时才分配即new的时候,子类继承父类很明显需要先给父类分配,没有父亲哪来的儿子是吧
题主可以通过编译后的
class
文件反编译来帮助理解初始化过程。在命令行中的指令
javap -l -c -p -v App
,执行后就会得到反编译后的内容,下面结合题主给的源码简单分析一下:下面展示和初始化有关的部分反编译内容
App
的class文件SubClass
的class文件SuperClass
的class文件大致解释一下内容(如果题主对
类,同时初始化静态变量并执行静态块,对应SubClass中JVM指令集
感兴趣可以看看JVM
规范,里面有很详细的解释)。上面反编译内容中星号部分是我加上的注解。按照代码的执行顺序:
1.JVM加载
App
类,App
中的静态变量在这时会初始化,对应App中调用App的构造函数
的字节码2.跳到
App
的构造函数,先初始化父类Object
,对应App中调用Object的构造函数
3.初始化实例变量
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
,对应App中调用SubClass的构造函数
4.此时要先加载
静态块
5.这时执行
中的代码打印出“1”
加载完成后执行构造函数,对应SubClass中6.
构造函数
7.
构造函数最开始是执行
的构造函数,对应SubClass中调用SuperClass的构造函数
8.执行🎜中构造函数中的代码打印出“得到字符串构造SuperClass”
9.🎜构造完成
return
到7中的代码位置,继续执行🎜剩余的代码10.执行🎜中构造函数代码打印出“2”
11.执行完🎜构造函数
return
到3中的代码位置,继续执行App
剩余的代码12.执行
App
中构造函数代码打印出“4”13.执行完
App
的构造函数return
到1中代码位置,继续初始化的静态变量14.执行静态块中的方法,答应出“3”
15.进入到
main
入口方法,打印出“Hello”🎜 🎜上面的过程就是大致的执行顺序。🎜 🎜回答的内容可能会有点混乱,题主看不懂的地方可以问我。🎜