多态(polymorphism)

多态

多态:不同的对象调用同一个方法可能会产生不同的行为

eg.

 

package com.chenxingen;
class Pet{
    String name;//宠物名
    public Pet() {
    }
    public Pet(String name) {
        this.name = name;
    }

    public void eat(){
        System.out.println("吃东西...");
    }
}
class Cat extends Pet{//猫类
    public void eat(){
        System.out.println("猫吃鱼...");
    }
}
class Horse extends Pet{//马类
    public void eat(){
        System.out.println("马吃草...");
    }
}
public class Work241 {
    //多态
    //使用父类作为方法形参
    public static void petEat(Pet pet){
        pet.eat();
    }
    public static void main(String[] args) {
        //子类到父类的自动转型
        Pet cat=new Cat();
        Horse horse=new Horse();
        petEat(cat);
        petEat(horse);
    }
}

 

注意:多态指的是方法,属性没有多态这个概念

 

多态存在的必要条件:、

1.继承

首先我们要知道多态的概念,是同一个方法,不同的对象调用会有不同的行为,也就是说不同的对象都必须要有这个同一的方法,显然,如果我们每个都自己定义一个名字相同的方法,是能够做到的,但显然不符合代码的复用性,而多态的存在就是提高代码的复用性,所以我们需要继承,只需要父类中有这个方法,其所有的子类都会有,所以多态存在的第一步必须要继承!

2.方法重写

我们已经实现里多态的第一步,继承!我们知道,我们继承下来的方法都是父类中的,父类写好的方法是不变的,这个时候我们的子类如果不进行重写该方法,那么尽管是不同的对象,当执行该方法的时候,其产生的行为还是一模一样,所以我们必须在子类继承有该方法的父类后,还要对该方法进行重写

3.父类引用指向子类

这个地方需要我们重点理解!!!(下面我们从便于理解的角度来阐述这一点)

如上示例代码中:

public class Work241 {
    //多态
    //使用父类作为方法形参
    public static void petEat(Pet pet){
        pet.eat();
    }
    public static void main(String[] args) {
        //子类到父类的自动转型
        Pet cat=new Cat();
        Horse horse=new Horse();
        petEat(cat);
        petEat(horse);
    }
}

我们知道Pet是父类,cat、horse是子类;

Pet cat=new Cat();          

这行代码的意思就是:创建一个猫对象,用一个宠物类型的变量类接收,从逻辑上,可以接受!

也就是说:猫是宠物

Cat cat=new Pet();          ×

这行代码的意思就是:创建一个宠物对象,用一个猫类型的变量类接收,从逻辑上,可以接受,但是有歧义,因为宠物不一定就是猫!

而机器语言与自然语言不同,java又是一门严谨的语言,所以这种写法错误!

 public static void petEat(Pet pet){
        pet.eat();
    }
petEat(cat);

这个理解起来应该没有问题,因为本身也是一个Pet类型的变量接受!

Horse horse=new Horse();
petEat(horse);

这个理解起来可能会有些一些问题,因为我们是用Horse变量类型来接受的,而方法中的形参是Pet类型的,两者不一样,但是还是那句话,我们把马当做宠物是完全没有问题的,所以可以这样使用,这种就叫自动类型转换,这也叫做父类引用指向子类对象

Cat cat=new Pet();

cat父类引用,new Pet()子类对象

 

上述就叫多态,可以看出,这样我们能够大大的提高代码的复用性,如何看出,可以比较

 public static void petEat(Pet pet){
        pet.eat();
    }

等于
    public static void petEat(Cat cat){
        cat.eat();
    }
    public static void petEat(Horse horse){
        horse.eat();
    }
     public static void petEat(... ...){
        ....eat();
    }

如果不适用多态,随着宠物类型的增多,我们的方法会逐渐增加,代码趋于繁琐…

 

补充:多态中常犯错误!!!

eg;(编译问题)

 

 seeDoor方法是子类Dog特有的方法,不是重写的方法

 

 此时,我们可以看到,编译报错了!

这是因为在编译的时候,是检查语法错误,而此时new Dog()是用一个动物变量来接受,这并没有错误,但是,在执行dog.seeDoor()时,报错了,因为dog是一个Animal变量,编译器需要去Animal类中查看是否存在一个叫seeDoor的方法,我们知道seeDoor方法是Dog类特有的,在Animal中没有,所以编译器报错了!

那如何处理这种情况呢?

 

 使用强转即可!

 

eg;(运行问题)

 

 我们创建里一个Dog对象,用Animal类型接收,然后将其强转为Dog类型,强转为Cat类型,都没有错误

因为动物可以是猫,也可以是狗,就像猜盲盒一样,我们知道这个盒子里要么是猫要么是狗,只有打开后,观察其特征,我们才知道其具体是啥,但是我们现在没有具体看到,只知道要么是猫,要么是狗,我们这样去说并没有错,也就是编译检查为什么通过的原因!

 

 问题来了,报错了,运行报错了,盒子打开了,我们知道有人猜错了,有人猜对了,那么我们现在再把盒子关闭,人们都知道里面是狗了!

为里防止这种情况,我们需要借助工具

Pet pet=new Cat();
        System.out.println(pet instanceof Pet);//TRUE
        System.out.println(pet instanceof Cat);//TRUE
        System.out.println(pet instanceof Horse);//FALSE

Instanceof是二元运算符,左边是对象,右边是类:当对象是右边类或者子类所创建对象时,返回true