JAVA继承中的初始化

之前在学习《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(); 才算完成,小小的一个创建对象的语句,里面却还有这么多的过程,当然里面还有更多的过程细节,就不在此说了,以后如果还继续探索应该会写在别的文章里~~

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值