Java——继承与初始化

本文介绍了Java中类的继承与初始化过程,包括构造器如何工作、如何正确地初始化基类及子类成员变量,并通过示例代码展示了不同情况下构造器的调用顺序。

初始化基类

在Java中,当初始化涉及到继承体系时会变得复杂一些。有继承时,现在涉及到子类与基类两个类,子类就像是一个与基类有相同接口的新类,或许还会有一些额外的方法和成员。但继承并不只是复制基类的接口,当创建一个子类的对象时,改对象包含了一个基类的子对象,这个子对象与直接用基类创建的对象是一样的。二者区别在于,后者来自于外部,而基类子对象被包装在导出类对象的内部。

对基类子对象的正确初始化也是至关重要的,而且只有一种方式来保证这一点:在构造器中调用基类的构造器来执行初始化,而基类构造器具有执行基类初始化所需要的所有知识和能力。Java会自动在导出类的构造器中插入对基类构造器的调用。

示例:
class Art{
    Art(){
        System.out.println("Art constructor");
    }
}

class Drawing extends Art{
    Drawing(){
        System.out.println("Drawing constructor");
    }
}
class Cartoon extends Drawing{
    Cartoon(){
        System.out.println("Cartoon constructor");
    }
    public static void main(String[] args) {
        new Cartoon();
    }
}

输出:

Art constructor
Drawing constructor
Cartoon constructor

 

从输出看到,Art类的构造器先执行了,然后是Drawing类的构造器,最后是Cartoon类的构造器被执行。构建过程是从基类向外扩散的,在导出类构造器可以访问之前,基类已经初始化完成。

 

有参数的构造器

如果基类中显示的定义了带参数的构造器,就不会有默认的无参数构造器了,这时就需要在子类的构造器中显示的调用基类的相应构造器,使用super关键字,配上适当的参数列表。否则编译器会报告基类的默认构造器未定义。

示例:
class Game {
    Game(int i){
        System.out.println("Game constructor");
    }
   
}
class BoardGame extends Game{
    BoardGame(int i){
        super(i);
        System.out.println("BoardGame constructor");
    }
}
class Chess extends BoardGame{
    Chess() {
        super(11);
        System.out.println("Chess constructor");
    }
   
    public static void main(String[] args) {
        new Chess();
    }
}
输出:

Game constructor
BoardGame constructor
Chess constructor

如果把Chess或BoardGame类构造器中的super语句注释掉,编译器会报错的。

 

继承与初始化

看示例:class Insect{
    private int i=9;
    protected int j;
    Insect(){
        System.out.println("i="+i+", j="+j);
        j=39;
    }
    private static int x1 = printInit("static Insect.x1 initilized");
    static int printInit(String s) {
        System.out.println(s);
        return 47;
    }
}
class Beetle extends Insect {
    private int k = printInit("Beetle.k initilized");
    public Beetle(){
        System.out.println("k="+k);
        System.out.println("j="+j);
    }
    private static int x2 = printInit("static Beetle.x2 initilized");
    public static void main(String[] args) {
        System.out.println("Beetle constructor");
        new Beetle();
    }
}
输出:

static Insect.x1 initilized
static Beetle.x2 initilized
Beetle constructor
i=9, j=0
Beetle.k initilized
k=47
j=39

解释输出:

程序运行第一件事就是访问Beetle.main方法,加载器会加载Beetle类的编译代码(在Beetle.class文件中),在加载Beetle.class时,发现它有一个基类Inset类,于是继续加载Insetc类的编译代码,不管是否打算产生一个Insect类的对象,这都要发生。如果Insect类还有它自己的基类,这个过程还要类推。

接着,根基类的static数据执行初始化,此处是Insect类中的static数据x1,初始化x1时通过调用静态方法printInit,

于是打印“static Insect.x1 initilized”。

然后是导出类Beetle的静态数据x2初始化,

打印static Beetle.x2 initilized。

 

类加载完毕,开始执行main方法,打印“Beetle constructor”。

接着创建Beetle对象,分配足够的存储空间,Beetle对象的各项数据清零。

然后调用构造器的时候会导致基类的构造器被调用初始化基类,这个过程会持续到根类的构造器。此处是Insect类。

对Insect类的数据成员执行初始化,使

i=9,j=0

执行Insect类的构造体部分,打印“i=9, j=0”,并使

j=39

基类初始化完毕,然后执行子类Beetle的数据成员k的初始化,通过继承的方法printInit初始化,打印“Beetle.k initilized”,然后返回一个整数47给k。

接着执行Beetle类的构造体,分别打印k和j的值:“k=47”,“j=39”

对象创建完毕。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值