当存在继承的情况时,各变量的初始化情况变得更为复杂了(不关局部变量的事),我们有必要梳理一下。
先上一段代码:
class Insect{
private int i = 9;
protected int j;
Insect(){
System.out.println("i = " + i + ",j = " + j);
}
private static int x1 = printInit("static Insect.x1 initialized");
public int x2 = printInit("Shape.x2 initialized");
static int printInit(String s) {
System.out.println(s);
return 47;
}
}
public class Beetle extends Insect{
private int k = printInit("Beetle.k initialized");
public Beetle(){
System.out.println("k = "+k);
System.out.println("j = "+j);
}
public int x1 = printInit("Circle.x2 initialized");
private static int x2 = printInit("static Beetle.x2 initialized");
public static void main(String[] args) {
System.out.println("Beetle constructor");
Beetle b = new Beetle();
}
}
输出结果:
static Insect.x1 initialized
static Beetle.x2 initialized
Beetle constructor
Shape.x2 initialized
i = 9,j = 0
Beetle.k initialized
Circle.x2 initialized
k = 47
j = 0
由输出结果可以看出:先初始化基类的静态域==》子类的静态域==》基类的非静态成员变量==》基类的构造方法==》子类的非静态成员变量==》子类的构造方法。
总结:
访问Beetle类时加载它的字节码时,发现它有一个基类,于是开始加载它的基类(即使你不打算生成基类的对象),如果基类还有基类,则以此类推。字节码加载完毕后,根基类的静态域会先初始化,然后导出类,因为导出类很可能依赖基类的成员。
至此,类加载完毕,对象就可以开始创建了。首先,对象的基本类型会被设置为0(等价0),引用对象被设置为null,之后基类的构造器被调用,导出类的构造器和基类的顺序一样。基类构造器调用后,实例变量按次序被初始化。