扫码关注官方订阅号
学习是最好的投资!
类变量(静态变量)>静态块>构造方法>普通成员变量>子类除静态外。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.初始化实例变量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”
JVM指令集
JVM
调用App的构造函数
Object
调用Object的构造函数
调用SubClass的构造函数
静态块
构造函数
调用SuperClass的构造函数
return
main
上面的过程就是大致的执行顺序。
回答的内容可能会有点混乱,题主看不懂的地方可以问我。
微信扫码关注PHP中文网服务号
QQ扫码加入技术交流群
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号
PHP学习
技术支持
返回顶部
类变量(静态变量)>静态块>构造方法>普通成员变量>子类除静态外。JAVA所有static修饰的都会在编译时分配空间,而对象只会在使用时才分配即new的时候,子类继承父类很明显需要先给父类分配,没有父亲哪来的儿子是吧
题主可以通过编译后的
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入口方法,打印出“Hello”上面的过程就是大致的执行顺序。
回答的内容可能会有点混乱,题主看不懂的地方可以问我。