Java中的多态

多态


一.向上转型(子类引用给父类)


在类的继承中我们写了这样的一个代码
Bird bird=new Bird("小小");
上述代码也可以写成
Bird bird=new Bird("小小"); Animal bird2=bird;
或者写成 Animal bird2=new bird("小小");
  此时bird2是一个父类的引用,指向一个子类(Bird)的实例,这种写法称为向上转型



向上转型发生的时机:
 ①直接赋值(上面所描述的情况)
 ②方法传参
 ③方法返回

方法传参

//定义动物
class Animal{
    public  String name;

    public Animal(String name){
        this.name=name;
        }

    public void eat(String food){
        System.out.println(this.name+"animaleat"+food);
    }
}

//鸟类继承动物类
class Bird extends Animal{
    public Bird(String name) {
        super(name);
    }
//方法重写
    @Override
    public void eat(String food) {
        System.out.println(this.name+"birdeat"+food);
    }
}

public class Test {
    public static void main(String[] args) {
        Bird bird=new Bird("小小");
        feed(bird);        //传参
    }
    
    public static void feed(Animal animal){
        animal.eat("谷子");
    }
}

//打印结果
小小birdeat谷子

  注意到上述代码feed方法形参是Animal类型,实际上它对应的是Bird的实例。

方法返回

public class Test {

    public static void main(String[] args) {
        Animal animal=findMyAnimal();
    }

    public static Animal findMyAnimal(){
        Bird bird=new Bird("小小");
        return bird;
   }
}

  
  上述代码findMyAnimal方法返回的是一个Animal类型的引用,但是实际上却是对应到了Bird的实例


二.动态绑定

 也称运行时绑定
 解决的问题就是当子类重写了父类的方法时,运行时会出现什么样的结果呢?

先观察下列代码:

//定义动物类
class Animal{
    protected String name;

    public Animal(String name){
        this.name=name;
    }

    public void eat(String food){
        System.out.println(this.name+"animaleat"+food);
    }
}


class Bird extends Animal{
    public Bird(String name) {
        super(name);
    }

    @Override
    public void eat(String food) {     //子类重写eat方法
        System.out.println(this.name+"birdeat"+food);
    }
}

public class Test {

    public static void main(String[] args) {
        Animal animal1 = new Animal("小小");
        animal1.eat("豆子");
        Animal animal2 = new Bird("大大");
        animal2.eat("豆子");
    }
}

//打印结果
小小animaleat豆子
大大birdeat豆子


  animal1和animal2虽然都是Animal的引用,但是animal1指向的是Animal的实例,animal2指向的是Bird类型的实例
  animal1和animal2分别调用eat方法,此时animal1调用的是父类的eat方法,animal2调用的则是子类的eat方法

  所以,在Java中,调用某个类的方法,到底是执行父类的方法还是子类的方法,要看究竟这个引用指的是父类对象还是子类对象。这个过程是程序运行时决定的,因此为动态绑定


发生动态(运行时绑定)条件:

①继承(父类要引用子类)
②通过父类的引用调用子类和父类同名的覆盖方法

这里有个概念是重写,那么重写和重载有什么区别呢?
重写和重载



使用多态有什么好处呢?

 ①类的调用者对类的使用成本进一步降低
 ②能够降低代码的“圈复杂度”,避免使用大量的if-else

圈复杂度?
  圈复杂度是描述一段代码复杂程度的方式。一段代码如果平铺直叙,那么就容易理解,而如果有很多的条件分支或者循环语句,就认为理解起来复杂
  计算一段代码中条件语句和循环语句出现的个数,这个个数就称为“圈复杂度”。若一个方法的圈复杂度太高,就需要考虑重构。

 ③可扩展能力更强



三.super关键字


  由于重写机制,调用的是子类的方法,如果需要在子类内部调用父类方法呢?这时候就可以使用super关键字

换句话数就是super就相当于是父类实例的引用

super常见用法:
 1.使用super来调用父类的构造器(要构造子类就需要先构造父类)
 2.使用super来调用父类的普通方法

参考代码:

...
...
class Bird extends Animal{
    public Bird(String name) {
        super(name);
    }

    @Override
    public void eat(String food) {
        super.eat(food);
        System.out.println(this.name+"birdeat"+food);
    }
}

//打印结果
大大animaleat豆子
大大birdeat豆子


  上述代码在子类的eat方法中加入了super关键字,所以先上父类调用eat方法。

在这里要区分this关键字和super关键字的区别:
在这里插入图片描述




四.在构造器中用重写的方法(慎用)

构造方法内是否可以发生运行时绑定?
观察下列代码:

class B{

//B的构造中由func方法
    public B(){
        func();
    }

    public void func(){
        System.out.println("B.fun()");
    }
}


class D extends B{
    private int num=1;

    @Override
    public void func() {     //重写父类的func方法
        System.out.println("D.func()"+num);
    }
}

public class Test{
    public static void main(String[] args) {
        D d=new D();
    }
}

//打印结果
D.func()0

  上述代码打印结果显示此时调用了子类的func方法,这是为什么呢?
 ①构造D对象的同时,会调用B的构造方法
 ②B的构造方法中调用了func方法,此时会触发动态绑定,会调用子类D中func方法
 ③此时D对象自身还没有构造,所以num处在未初始化的状态,值为0

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值