JAVA中,构造方法明明有super,为什么还是父类的构造方法先于子类构造方法加载?

我的认知里,JAVA中,如果main方法中初次实例化一个对象,那会先在方法区加载这个对象所属的类的字节码文件,并且在静态区域中加载静态变量和静态代码块,然后同理加载子类的字节码文件和静态内容。

之后JVM会在堆内存中开辟空间给这个对象,这个对象再初始化父类的普通变量和构造代码块,然后分配栈帧给父类构造方法压栈执行,执行完后把信息传给堆中的对象然后弹栈。再之后才会初始化子类的普通变量和构造代码块,分配栈帧给子类构造代码块执行,然后传出信息弹栈。经历完这些,一个对象才算是被建立好。

可是子类的构造方法第一行会加入super语句去执行父类的构造方法,那是不是意味着当子类的构造方法在栈中开辟了内存之后,才开辟了父类构造方法的内存空间?这不就冲突了吗?

而且在父类初始化过程中,会调用到构造方法,但是子类会使用到哪一个构造方法按道理来说这时候还没运行到子类的super语句所以该使用哪一个构造方法应该是未知的。

所以我的认知应该有些问题,希望大家帮我纠正,谢谢!

参考GPT和自己的思路:在Java中,当子类继承了父类后,如果在子类的构造方法中没有显式地调用父类的构造方法,那么编译器会默认调用父类的无参构造方法。如果子类的构造方法中使用了super()语句,则会先调用父类的构造方法,然后再执行子类的构造方法。因此,子类的构造方法中的super()语句会在子类的构造方法执行之前调用父类的构造方法。这也正是为什么父类的构造方法先于子类构造方法加载的原因。

在加载子类的字节码文件和静态内容时,如果子类有父类,则会先加载父类。而在初始化子类的普通变量和构造代码块时,也必须先初始化父类的变量和构造代码块。在执行子类的构造方法时,如果使用了super()语句,则会先执行父类的构造方法,然后再执行子类的构造方法。

因此,使用super()语句不会影响子类的内存分配,也不会影响构造方法的执行顺序。在执行父类的构造方法时,子类的内存空间已经被分配好了,子类的构造方法也已经在栈中开辟了内存空间,只是还没有开始执行而已。执行子类的构造方法之前,会先执行父类的构造方法,这是Java继承机制中的一个规定。

你的理解中有一些错误,下面我来逐一解释一下。


  1. 首先,在 Java 中,当程序启动时,JVM 会加载并初始化所有的类。在类的加载过程中,类的字节码文件会被加载到方法区,而静态变量和静态代码块会被初始化。这一点是正确的。

  2. 然后,在创建一个对象时,JVM 会先为对象分配内存空间,并初始化对象的成员变量。在初始化成员变量时,如果该变量是静态变量,那么它的值已经在类的加载过程中被初始化了。如果该变量是实例变量,那么它的值会被初始化为默认值(例如 int 类型的变量会被初始化为 0)。

  3. 接着,在调用构造方法时,JVM 会先执行父类的构造方法,然后再执行子类的构造方法。在执行父类的构造方法时,如果父类也有父类,那么会依次执行祖先类的构造方法,直到执行完最顶层的 Object 类的构造方法。这一点也是正确的。

  4. 此外,在子类的构造方法中使用 super 关键字调用父类的构造方法时,必须放在第一行,确保父类的构造方法先被执行。在执行父类的构造方法时,父类的实例变量和构造代码块会被初始化。然后再执行子类的实例变量和构造代码块的初始化。

  5. 最后,需要注意的是,子类的构造方法中必须调用父类的构造方法,否则会出现编译错误。如果子类没有显式地调用父类的构造方法,那么编译器会自动在子类的构造方法中添加 super() 调用父类的无参构造方法。

  6. 综上所述,你的理解中有一些错误,但也有一些是正确的。需要注意的是,在 Java 中,对象的初始化过程是比较复杂的,需要考虑到继承关系、静态变量、实例变量、构造方法等多个因素。如果你还有疑问,可以在评论中继续提出。

任何情况都必须先构造基类,然后才构造派生类
调用super之前,其实基类已经构造好了。

浅谈JVM
上面这个专题你看前三章应该就能明白了