继承关系下创建对象的具体流程
public class Person {int x = initX(); // 显式初始化:调用方法 initX()public Person() {System.out.println("Parent 构造器执行, x = " + x);}int initX() {System.out.println("initX() 被调用了");return 100;}
}public class Child extends Person {int y = initY();private int initY() {return 20;}public Child() {this("张三");System.out.println("Child 构造器执行");}public Child(String name) {System.out.println("有参构造器执行");}
}
Child的字节码文件:
// class version 65.0 (65)
// access flags 0x21: public, super
public class com/itheima/test/Child extends com/itheima/test/Person {// compiled from: Child.java
// access flags 0x0: package-private field
I y // 字段 y,类型 int,用于存储 initY() 方法返回的值// access flags 0x2: private method
private initY()I // 私有方法 initY,返回 int
L0
LINENUMBER 7 L0 // 对应源代码第 7 行
BIPUSH 20 // 将常量 20 推入操作数栈
IRETURN // 返回栈顶 int 值(20)
L1
LOCALVARIABLE this Lcom/itheima/test/Child; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1 // 方法局部变量只有一个:this// access flags 0x1: public constructor
public <init>()V // 无参构造器
L0
LINENUMBER 11 L0 // 对应源代码第 11 行
ALOAD 0 // 将 this 引用推入栈
LDC "\u5f20\u4e09" // 将字符串常量 "张三" 推入栈
INVOKESPECIAL com/itheima/test/Child.<init> (Ljava/lang/String;)V
// 调用本类带 String 参数的构造器
L1
LINENUMBER 12 L1 // 对应源代码第 12 行
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "Child \u6784\u9020\u5668\u6267\u884c" // "Child 构造器执行"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
// 打印日志
L2
LINENUMBER 13 L2 // 对应源代码第 13 行
RETURN // 构造器返回
L3
LOCALVARIABLE this Lcom/itheima/test/Child; L0 L3 0
MAXSTACK = 2
MAXLOCALS = 1 // this// access flags 0x1: public constructor with String
public <init>(Ljava/lang/String;)V
L0
LINENUMBER 15 L0 // 对应源代码第 15 行
ALOAD 0 // 将 this 推入栈
INVOKESPECIAL com/itheima/test/Person.<init> ()V
// 调用父类 Person 的无参构造器
L1
LINENUMBER 4 L1 // 对应 Person 构造器或字段初始化前的行号标记
ALOAD 0 // 将 this 推入栈
ALOAD 0 // 再次将 this 推入栈
INVOKEVIRTUAL com/itheima/test/Child.initY ()I
// 调用本类私有方法 initY(), 返回 20
PUTFIELD com/itheima/test/Child.y : I
// 将返回值存入字段 y
L2
LINENUMBER 16 L2 // 对应源代码第 16 行
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "\u6709\u53c2\u6784\u9020\u5668\u6267\u884c" // "有参构造器执行"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
// 打印日志
L3
LINENUMBER 17 L3 // 对应源代码第 17 行
RETURN // 构造器返回
L4
LOCALVARIABLE this Lcom/itheima/test/Child; L0 L4 0
LOCALVARIABLE name Ljava/lang/String; L0 L4 1
MAXSTACK = 2
MAXLOCALS = 2 // this, name
}
Person的字节码:
// class version 65.0 (65)
// access flags 0x21: ACC_PUBLIC | ACC_SUPER
public class com/itheima/test/Person {// compiled from: Person.java
// access flags 0x19: ACC_PUBLIC | ACC_FINAL | ACC_STATIC
public final static INNERCLASS java/lang/invoke/MethodHandles$Lookupjava/lang/invoke/MethodHandles Lookup
// access flags 0x0: package-private
I x // 字段 x,类型 int,用于存储 initX() 返回的值// access flags 0x1: ACC_PUBLIC
public <init>()V // Person 类的无参构造器
L0
LINENUMBER 6 L0 // 对应源代码第 6 行
ALOAD 0 // 将 this 引用压入操作数栈
INVOKESPECIAL java/lang/Object.<init> ()V
// 调用父类 Object 的构造器
L1
LINENUMBER 4 L1 // 源代码第 4 行,准备初始化 x 字段
ALOAD 0 // 将 this 再次压入栈
ALOAD 0 // 将 this 再次压入栈(为后续调用 initX() 提供 this)
INVOKEVIRTUAL com/itheima/test/Person.initX ()I
// 调用本类包私有方法 initX(),返回 int
PUTFIELD com/itheima/test/Person.x : I
// 将 initX() 的返回值存入 this.x
L2
LINENUMBER 7 L2 // 源代码第 7 行,打印构造信息
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
// 获取 System.out
ALOAD 0 // 将 this 压入栈,为取 x 字段做准备
GETFIELD com/itheima/test/Person.x : I
// 读取 this.x 的值
INVOKEDYNAMIC makeConcatWithConstants(I)Ljava/lang/String; [
// 使用 invokedynamic 生成字符串连接 "Parent 构造器执行, x = {0}"
// 第一个参数 MethodHandles.Lookup
// 第二个参数 调用点名称
// 第三个参数 方法类型 (I)String
// 第四个参数 模板 "Parent 构造器执行, x = \u0001"
]
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
// 打印拼接后的字符串
L3
LINENUMBER 8 L3 // 源代码第 8 行,结束构造
RETURN // 构造器返回
L4
LOCALVARIABLE this Lcom/itheima/test/Person; L0 L4 0
// 方法局部变量表:索引0 为 this
MAXSTACK = 2 // 最大操作数栈深度
MAXLOCALS = 1 // 最大局部变量数量// access flags 0x0: package-private
initX()I // 包私有方法 initX,返回 int
L0
LINENUMBER 11 L0 // 源代码第 11 行
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
// 获取 System.out
LDC "initX() 被调用了" // 将字符串常量推入栈
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
// 打印日志:initX() 被调用了
L1
LINENUMBER 12 L1 // 源代码第 12 行
BIPUSH 100 // 将常量 100 推入栈
IRETURN // 返回栈顶 int 值(100)
L2
LOCALVARIABLE this Lcom/itheima/test/Person; L0 L2 0
// 方法局部变量表:索引0 为 this
MAXSTACK = 2 // 最大操作数栈深度
MAXLOCALS = 1 // 最大局部变量数量
}
当new Child()的具体流程图:
new Child()
└─ 为 child 对象分配内存,字段 x、y 等都置为默认值 (0)Child() ──┬─ this("张三")
│
└─ Child(String name)
└─ 调用 Person.<init>()
├─ super() → 调用 Object.<init>()
├─ 执行父类显式初始化:
│ └─ 调用 initX()
│ ├─ 打印 "initX() 被调用了"
│ └─ 返回 100
│ └─ PUTFIELD x = 100
├─ 打印 "Parent 构造器执行, x = 100"
└─ 返回 Person.<init>()├─ 执行子类显式初始化:
│ └─ 调用 initY()
│ └─ 返回 20
│ └─ PUTFIELD y = 20
├─ 打印 "有参构造器执行"
└─ 返回 Child(String)└─ 回到 Child():
└─ 打印 "Child 构造器执行"
值得注意的是:子类会调用父类的构造方法,父类会调用Object的构造方法,然后才会给给自的成员变量显示初始化,然后构造方法初始化.