当创建任何 Java 对象时,程序总会先依次调用每个父类非静态初始化块、父类构造器(总是从 Object 开始,因为Java 程序里所有类的最终父类都是 java.lang.Object 类)执行初始化,最后才调用本类的非静态初始化块、构造器执行初始化。
当调用某个类的构造器来创建Java 对象时,系统总会先调用父类的非静态初始化块进行初始化。这个调用是隐式执行的,而且父类的静态初始化块总是会被执行。接着会调用父类的一个或多个构造器执行初始化,这个调用既可以是通过 super 进行显式调用,也可以是隐式调用。
当所有父类的非静态初始化块、构造器依次调用完成后,系统调用本类的非静态初始化块、构造器执行初始化,最后返回本类的实例。 下面程序演示了创建Java 对象时的初始化过程。
class Creature { { System.out.println("Creature 的非静态初始化块"); } //下面定义两个构造器 public Creature() { System.out.println("Creature 无参数的构造器"); } public Creature(String name) { //使用 this 调用另一个重载、无参数的构造器 this(); System.out.println("Creature 带有 name参数的构造器,name参数:" + name); } } class Animal extends Creature { { System.out.println("Animal的非静态初始化块"); } public Animal(String name) { super(name); System.out.println("Animal带一个参数的构造器,name 参数:" + name); } public Animal(String name , int age) { //使用 this 调用另一个重载的构造器 this(name); System.out.println("Animal 带 2个参数的构造器,其age:" + age); } } class Wolf extends Animal { { System.out.println("Wolf 的非静态初始化块"); } public Wolf() { //显式调用父类有2个参数的构造器 super("灰太狼", 3); System.out.println("Wolf 无参数的构造器"); } public Wolf(double weight) { //使用 this 调用另一个重载的构造器 this(); System.out.println("Wolf 的带 weight 参数的构造器,weight 参数:" + weight); } } public class InitTest { public static void main(String[] args) { new Wolf(5.6); } }
上面程序定义了 Creature、Animal、Wolf 这 3 个类,其中 Animal 是 Creature 的子类,Wolf 是Animal 的子类。3个类都包含了非静态初始化块、构造器成分。当程序调用Wolf 的指定构造器创建Wolf 实例时,程序会按上面介绍的初始化步骤执行初始化。
只要在程序创建Java 对象,系统总是先调用最顶层父类的初始化操作,包括初始化块和构造器,然后依次向下调用所有父类的初始化操作,最终执行本类的初始化操作返回本类的实例。至于调用父类的哪个构造器执行初始化,则分为如下几种情况:
- 子类构造器执行体的第一行代码使用super 显式调用父类构造器,系统将根据super调用里传入的实参列表来确定调用父类的哪个构造器;
- 子类构造器执行体的第一行代码使用 this 显式调用本类中重载的构造器,系统将根据 this 调用里传入的实参列表来确定本类的另一个构造器(执行本类中另一个构造器时即进入第一种情况);
- 子类构造器执行提中既没有super调用,也没有this 调用,系统将会在执行子类构造器之前,隐式调用父类无参数的构造器。
super 调用用于显式调用父类的构造器,this 调用用于显式调用本类中另一个重载的构造器。super调用和this 调用都只能在构造器中使用,而且super 调用和this 调用都必须作为构造器的第一行代码,因此构造器中的super调用和this 调用最多只能使用其中之一,而且最多只能调用一次。
转载请注明:零五宝典 » Java中父类构造器的隐式调用和显式调用