构造器与多态


我们在文章 Java 的继承 中已经介绍了,子父类的构造器的加载过程,是按照继承层次逐层向上,在子类的构造器中,如果没有明确指明调用父类的哪一个构造器,那么就会调用默认构造器,确保每个父类的构造器都能得到调用,如果父类中不存在默认构造器,那么编译器就会报错。

构造器是特殊的方法,实际上是隐式的 static 方法,当构造器涉及到多态的时候,就有必要了解构造器是怎样通过多态在复杂的层次结构中运作,嗯,很有必要。

先回顾一下,上代码:

public class Sport{

    public Sport() {
        System.out.println("sport constructor");
    }
}

class Ball extends Sport{

    public Ball() {
        System.out.println("ball constructor");
    }
}

class Baseball extends Ball {

    private Sport sport = new Sport();
    private Ball ball = new Ball();

    public Baseball() {
        System.out.println("baseball constructor");
    }

    public static void main(String[] args) {
        new Baseball();
    }
}

大家可以先想想输出会是什么。

我们可以看到有三层继承,当我们调用 Baseball 的构造器的时候,编译器回去寻找其父类 Ball,然后发现 Ball 还有父类 Sport,Sport 类就没有父类了,那么就先调用 Sport 的构造器,然后调用 Ball 的构造器;然后按照顺序初始化 Baseball 的成员变量,首先是 ball,定义的时候就初始化了,初始化的时候发现有父类,先调用父类 Sport 构造器,然后调用自己的构造器,然后初始化 sport 对象,由于没有父类,直接调用父类构造器;最后才是运行 Baseball 自己的构造器。所以打印结果如下:

sport constructor
ball constructor
sport constructor
ball constructor
sport constructor
baseball constructor

如果父类中有成员变量或方法呢,先看这段代码:

public class Sport {

    private Description des = new Description("sport");

    public Sport() {
        System.out.println("sport constructor");
    }

    public void play() {
        System.out.println("play sport");
    }
}

class Ball extends Sport {

    private Description des = new Description("ball");

    public Ball() {
        System.out.println("ball constructor");
    }

    /**
     * 重写 play 方法
     */
    public void play() {
        System.out.println("play ball");
        super.play();
    }
}

class Baseball extends Ball {

    private Description des = new Description("baseball");

    public Baseball() {
        System.out.println("baseball constructor");
    }

    /**
     * 重写 play 方法
     */
    public void play() {
        System.out.println("play baseball");
        super.play();
    }

    public static void main(String[] args) {
        Baseball baseball = new Baseball();
        baseball.play();
    }

}

class Description {

    private String str;

    public Description(String str) {
        this.str = str;
        System.out.println("description:" + str);
    }
}

运行 Baseball 中的 main 方法,到底会看到啥,我们先理一下。

首先是 Baseball 的构造器,我们上面讲过了,会逐层往上找,当找到 Sport 类的时候到头了,发现这个类还有成员变量 des,先初始化,然后才是 Sport 的构造器,接着往下到 Ball 类,类似,先初始化成员变量 des,然后执行 Ball 的构造器,接着到 Baseball 类,发现也有成员变量,没办法,先初始化然后才是构造器。终于到 play() 方法了,顺序执行,发现有 super,然后执行父类 Ball 的 play 方法,仍然发现有 super,依次向上。所以最终结果是:

description:sport
sport constructor
description:ball
ball constructor
description:baseball
baseball constructor
play baseball
play ball
play sport

我们可以看到,构造器初始化的时候,逐层往上(找父类),然后从上往下执行(从父类到子类),如果父类中有成员变量,那么先初始化成员量,然后才是构造器。当运行方法的时候,通过 super 关键字顺序执行,从下往上执行(从子类到父类)。

刺激的部分来了,我们知道,在一个方法内部,动态绑定是在运行的时候才决定的,因为对象无法知道它属于当前类还是属于它的子类。如果要调用构造器里面的一个动态绑定方法,就需要用到被覆盖的方法,那么问题来了,如果被覆盖的方法在对象被完全初始化之前调用,那么就会出现问题。比如下面这段代码:

public class Sport {

    public Sport() {
        System.out.println("sport constructor");
        //大家猜猜这个方法是调用 Sport 类的还是 Ball 类的
        play();
    }

    public void play() {
        System.out.println("play sport");
    }
}

class Ball extends Sport {

    private String str = "str";

    public Ball(String str) {
        this.str = str;
        System.out.println("ball constructor, str:" + str);
    }

    /**
     * 重写 play 方法
     */
    public void play() {
        System.out.println("play ball, str:" + str);
    }

    public static void main(String[] args) {
        new Ball("ball");
    }
}

我们运行 Ball 的 main 方法,会看到如下结果:

sport constructor
play ball, str:null
ball constructor, str:ball

我们发现父类构造器中调用的是子类的 play 方法,但是结果是不是出乎意料,str 的值居然是 null。为什么会出现这种情况,明明已经进行了初始化,脸呢?

冷静一下,我们理下思路,首先调用 Ball 的有参构造器,往上找,发现有父类,然后调用父类 Sport 的构造器,执行 play 方法,这里就需要注意一下,执行的是子类 Ball 的 play 方法,打印变量 str 的值,这时候 Ball 的有参构造器还没有执行,所以没有重新赋值,直接打印初始值,没毛病,至于结果为啥是 null,这是因为父类构造器再调用子类的方法时,子类这时候还没有完成初始化,所以成员变量 str 还是初始值,这个初始值是 null,而不是赋给它的 “str”。等父类的构造器执行完成后,然后才是子类的成员变量,先初始化为 “str”,然后执行有参构造器,str 的值变为 “ball”。

这个坑就介绍到这了。

欢迎关注公众号: 一盐难进

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值