之前在学习《Java核心技术-卷一》时,关于继承基类的子类的初始化过程一直弄不明白,今天学习了《Java编程思想》的第7章,好像终于有点明白了。
继承基类的子类(以下简称子类)的对象创建的初始化过程如下:
1.加载子类的.class文件;
2.通过关键字得知子类有一个基类,继续加载基类的.class文件;若基类还有它的上一层基类则会继续加载;
3.static域的初始化是在类加载完之后就会进行的,最上一层基类加载完,该类的static域便会初始化,此时基类已加载完,static域也已经初始化完毕,再处理第二层基类的static域,对其进行初始化,直到最下层子类的static域初始化完成,此时子类及其所有基类的加载和static域已经初始化完成。
以上三步是在没有创建对象,或者说子类的main方法里没有任何语句时都会执行的,只要运行子类,都会执行类的加载和静态域的初始化。
4.静态域和实例域的初始化时机不同,静态域的初始化是在类加载完就一定会进行的,但是实例域的初始化是在创建对象之后才进行的。new Beatles();这条语句创建一个Beatles类的对象,此时才会实现实例域的初始化,也就是说,只有在调用构造器时才会进行实例域的初始化。
5.在子类调用构造器时,会最先调用基类的构造器,在调用基类的构造器时,基类的实例域便被初始化了,基类还存在其上一层基类,在调用它的构造器时,依然会调用其上一层基类的构造器。这一层的构造器调用时触发实例初始化,之后调用构造器,接着下一层的构造器触发该层的实例初始化,再调用构造器,可以看到,构造器的调用会触发实例域初始化,先初始化实例域,再调用构造器。
测试代码如下
class Insect{
private int i=9;
protected int j;
Insect(){
System.out.println("i="+i+","+"j="+j);
j=39;
System.out.println("i="+i+","+"j="+j);
}
private static int x1=printInit("static Insect.x1 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);
}
private static int x2=printInit("static Beetle.x2 initialized");
}
class Beatles extends Beetle{
private int m=printInit("beatles.m initialized");//为了证明static和非static域的初始化时机不一样
private static int x3=printInit("static Beatles.x3 initialized");
public Beatles(){
System.out.println("m="+m);
}
public static void main(String[] args) {
System.out.println("beatles initialized");
Beatles b3=new Beatles();//加载类之后static域初始化结束,只有创建对象,调用构造器的时候才会进行初始化。
System.out.println("x3="+b3.x3);
//System.out.println(m);
}
}
运行结果:
static Insect.x1 initialized
static Beetle.x2 initialized
static Beatles.x3 initialized
beatles initialized
i=9,j=0
i=9,j=39
Beetle.k initialized
k=47
j=39
beatles.m initialized
m=47
x3=47
从运行结果可以看出:在main方法里的第一句 System.out.println("beatles initialized");之前Insect类的静态域x1、Beetle的静态域x2,Beatles的静态域x3 就已经被初始化,而且如果把main方法里的语句全部注释掉再运行,运行结果里依然会有这三行,这表明static域的初始化与创建对象无关,只是运行main方法就会实现其初始化。而且从其顺序可以看出来,是先进行了最上一层基类的初始化,再进行第二层,最后是子类的static域初始化。
进入main方法后,执行第一句beatles initialized,接下来的i=9,j=0 是基类insect的构造器中的语句,由于i已经被初始化成了给定值,j初始化成了默认值,说明在调用构造器之前,i和j已经被初始化了,也就是调用构造器触发该类的实例域初始化,接着便执行构造器里的语句,打印i和j的值,还有之后的语句就不再分析。
当insect这层类的实例初始化以及构造器调用完成后,会实现第二层Beetle类的实例初始化和构造器调用,从语句Beetle.k initialized也可以看出在调用该层的构造器之前,该层类的实例域k被初始化了,因此打印出了信息,接下来的k和j的打印信息,就是调用Beetle类的构造器了。
最后一层子类Beatles,也是一样,构造器调用之前,先实例域被初始化,打印信息,再调用构造器。
至此,一句简单的new Beatles(); 才算完成,小小的一个创建对象的语句,里面却还有这么多的过程,当然里面还有更多的过程细节,就不在此说了,以后如果还继续探索应该会写在别的文章里~~