先说结论
执行顺序:父主构造 -> 父类init -> 父次构造 -> 子主构造 -> 子init -> 子次构造
代码示例
创建了两个类,Child继承Parent
open class Parent(var name: String, var age: Int) {
constructor(name: String) : this(name, 30) {
println("Parent constructor")
}
init {
println("Parent init")
}
}
class Child(name: String, age: Int) : Parent(name, age) { //调用父类的主构造函数
constructor(name: String) : this(name, 10) {
println("Child constructor")
}
init {
println("Child init")
}
}
fun main(){
val child1 = Child("James") //通过子类次构造函数创建对象
val child2 = Child("Harden", 12) //通过子类主构造函数创建对象
}
运行结果
Parent init
Child init
Child constructor
============
Parent init
Child init
可以看出父类代码总是在子类之前执行。反编译一下代码看看
反编译
public class Parent {
public Parent(@NotNull String name, int age) {
Intrinsics.checkNotNullParameter(name, "name");
super();
this.name = name;
this.age = age;
String var3 = "Parent init"; //init代码块直接在移到了主构造函数之后
System.out.println(var3);
}
public Parent(@NotNull String name) {
Intrinsics.checkNotNullParameter(name, "name");
this(name, 30); //调用主构造函数
String var2 = "Parent constructor"; //子构造函数内的代码
System.out.println(var2);
}
}
public final class Child extends Parent {
public Child(@NotNull String name, int age) {
Intrinsics.checkNotNullParameter(name, "name");
super(name, age);
String var3 = "Child init"; //init代码块直接在移到了主构造函数之后
System.out.println(var3);
}
public Child(@NotNull String name) {
Intrinsics.checkNotNullParameter(name, "name");
this(name, 10);
String var2 = "Child constructor";
System.out.println(var2);
}
}
反编译代码可以看出来,在java代码中直接将init代码块拷贝到了主构造函数最后,也就是主构造函数执行完就会执行init,而在次构造函数又调用了主构造函数,所以执行顺序是主构造函数->init代码块->次构造函数。
在例子val child1 = Child("James")中,通过子类次构造函数创建对象,会先调用子类的主构造函数,在子类的主构造函数中会调用父类的主构造函数,而init正是在主构造函数执行完之后调用的。所以打印了
Parent init
Child init
Child constructor
如果子类调用的父类的次构造函数呢?也就是说把代码class Child(name: String, age: Int) : Parent(name, age)改为class Child(name: String, age: Int) : Parent(name)。打印结果如下:
Parent init
Parent constructor
Child init
Child constructor
可以看出父类的主构造函数,init函数,次构造函数总是比子类的先执行。