深入理解OOP——多态

多态

相同类型的不同对象,调用同一个方法,最终执行结果是不同的

多态的前提:

  • 子类继承父类
  • 子类重写父类中的方法
  • 父类的引用指向子类的方法

注意,类实现接口,这是一种特殊形式的继承,多态也可以体现在类和接口的关系中。

编译时多态和运行时多态的区别

编译时多态:即在编译时就能够确定调用哪个方法。

运行时多态:只有在运行时才能确定调用哪个方法。
编译时多态 和 运行时多态在 重载 和 重写上的区别。

在方法重载时,都是编译时多态。在编译期可根据参数的数据类型、个数以及次序来确定调用方法
在方法重写中,当子类对象引用自身类实例方法时,为编译时多态。但是当父类对象引用子类实例方法时(也就是上转型对象时,父类声明,子类实例化),为运行时多态,因为此时只有在运行时才可以去匹配到对应方法进行调用。

多态的应用

public class Animal {
    public void eat(){}

    public static void main(String[] args) {
        //父类引用指向子类对象
        Animal cat = new Cat();
        cat.eat();
        Animal dog = new Dog();
        dog.eat();
    }
}

class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("我是猫");
    }
}
class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("我是狗");
    }
}

一个父类型的引用,可以指向它的任何一个子类对象,指向哪个子类就调用那个子类的方法

关键点在于,在调用eat方法的时候,引用person指向的对象是谁

public class Game {
    public void play(){

    }

    public static void main(String[] args) {
        Game b =new BasketBall();
        b.play();
        Game f=new Football();
        f.play();
    }

}
class BasketBall extends Game{
    public void play(){
        System.out.println("篮球游戏。。。。");
    }
}
class Football extends Game{
    public void play(){
        System.out.println("足球游戏。。。。");
    }
}

增加或减少游戏种类变得更加方便;

使用多态,这时候想新增一个球类游戏,并且去运行它,几乎不需要修改什么代码,只需要新增这个类即可

instanceof关键字

在使用多态的情况下,instanceof关键字显得特别重要,因为它告诉我们,当前父类的引用,到底是执行的哪一个子类对象

通过instanceof关键字进行判断即可知道引用p到底接收的是一个类型的对象:

public void test(Person p){
    if(p instanceof Student){
    //说明p指向的是Student类型对象
    }
    else if(p instanceof Teacher){
    //说明p指向的是Teacher类型对象
    }
}

引用类型转换

向上转型(子类转父类)

多态本身就是将子类的对象赋值给父类的引用,这就是一个自动的向上转换的过程(类型自动转换)

例如:Person p = new Student();

向下转型(父类转子类)

父类类型向子类类型转换,是向下转换的过程。(类型强制转换)最好先用instanceof判断是否属于该类,然后再转换,

例如:

Person p = new Student();
Student s = (Student)p; //向下转型

为什么需要做向下类型转换:


当使用多态方式调用方法时,编译器会先检查父类中是否有该方法,如果父类中没有,则编译报错;

在这种情况下,即使子类中有这个方法,也是无济于事,因为编译器这一关都过不了,更不要说去运行

也就是说,在使用多态的情况下,父类引用是无法调用到子类中独有的方法;

public class Animal {
    public void eat(){}
    
    public static void main(String[] args) {
        Animal bird = new Bird();
        bird.fly();   //父类无fly方法,报错

        Bird bird1 = (Bird) bird; //向下转型,获得子类特有方法
        bird1.fly();
    }
}
class Bird extends Animal {
    @Override
    public void eat() {
        System.out.println("我是鸟,我能吃东西");
    }
    //子类特有方法
    public void fly() {
        System.out.println("我是鸟,我能飞行");
    }
}

引用类型强制转换时的异常:

在类型强制转换的过程中,可能会遇到类型转换异常,例如:

public class Person {
}

class Students extends Person{
    //注意,这是子类中单独定义的方法,和父类无关
    public void study(){}
}

class Teacher extends Person{

    public static void main(String[] args) {
        Person p = new Teacher();
//编译通过,运行报错!
//错误类型是:ClassCastException
        Students s = (Students)p;
        s.study();
    }
}

因为Teacher对象和Student类型没有任何子父类的关系,类型转换需要有父子类关系

加上判断条件,代码如下,解决报错

public static void main(String[] args) {
        Person person = new Teacher();

        //person如果属于Students类着返回true,执行下面代码
        if(person instanceof Students){ 
            Students s = (Students)p;
            s.study();
        }
    }

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值