java中,创建子类对象时,父类对象会也被一起创建么?

如果没有创建父类对象,子类如何使用父类的成员呢? 还有,super所谓的父类存储空间的表示到底是什么意思?
关注者
291
被浏览
111,910
登录后你可以
不限量看优质回答私信答主深度交流精彩内容一键收藏

要从 JVM 和编译器的角度来看这个问题,先放答案:

  • 创建子类对象时,不会创建父类对象,只会一直调用父类的构造函数(即 <init> 函数),直到调用到 Object 类的构造函数(即 Object.<init> )
  • super 这个关键字仅存在于编译器中,编译后就只剩下了 CONSTANT_Fieldref_info 和 CONSTANT_Methodref_info

以下是粗略实现细节

  • 创建子类对象时,先使用 new 分配内存,然后使用 invokespecial 调用子类自身的 <init> ,在子类的 <init> 中有一条编译器插入的 invokespecial 用于调用父类的 <init>,以此类推,直到调用到 Object.<init>
  • 关于 super 的处理,完全发生在编译器的名称解析阶段,该阶段过后,super 关键字已经完全无用(可以说是被解糖了)。在最后生成的字节码里,通过 super 使用的父类的成员已经变成了常量池里的 Methodref_info 和 Fieldref_info,这两个 info 里就包含了目标成员所属的类和它的类型信息。

比如在 Child 类中使用 super.fn() 调用父类 Base 中的 String fn(int, int) 函数。在最终的字节码就是这样的东西

invokespecial #1

此处的这个 #1 就是要调用的函数的 Methodref_info 在指常量池中的位置,而这个 Methodref_info 应该长这样(为了简洁,省略了其中的 Class_info、NameAndType_info 以及 Utf8_info,具体内容可以查看 JVM 相关规范):

Methodref_info {
  class: "LBase;"
  nameAndType: {
    name: "fn",
    descriptor: "(II)Ljava/lang/String;"
  }
}

从上面结构中的 class 那一项我们可以看到,这次调用的函数是 Base 类中的 fn,而不是 Child 类中的 fn,那么 JVM 在遇到这条 invokespecial 指令的时候,就会去解析常量池中的这个 Methodref_info,然后根据其中的信息最终会在 Base 对应的 InstanceKlass 中找到这个符合上面描述的函数,接着结合 aload_0 将 this 入栈,最后执行函数中的字节码。