多【多种】态【状态】,方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。

多态的具体体现

1) 方法的多态(重写和重载就体现多态)

2)对象的多态 (核心,困难,重点)

● 方法的多态案例

public class PloyMethod {
    public static void main(String[] args) {
        //方法重载体现多态
        A a = new A();
        //这里我们传入不同的参数,就会调用不同 sum 方法,就体现多态
        System.out.println(a.sum(10, 20));
        System.out.println(a.sum(10, 20, 30));
        //方法重写体现多态
        B b = new B();
        a.say();
        b.say();
    }
}

class B { //父类
    public void say() {
        System.out.println("B say() 方法被调用...");
    }
}

class A extends B {//子类

    public int sum(int n1, int n2) {//和下面 sum 构成重载
        return n1 + n2;
    }

    public int sum(int n1, int n2, int n3) {
        return n1 + n2 + n3;
    }

    public void say() {
        System.out.println("A say() 方法被调用...");
    }
}

● 属性没有重写之说!属性的值看编译类型

public class PolyDetail02 {
    public static void main(String[] args) {
        //属性没有重写之说!属性的值看编译类型
        Base base = new Sub();//向上转型
        System.out.println(base.count);// ? 看编译类型 10
        Sub sub = new Sub();
        System.out.println(sub.count);//? 20
    }
}

class Base { //父类
    int count = 10;//属性
}

class Sub extends Base {//子类
    int count = 20;//属性
}

● 对象的多态案例

(1)一个对象的编译类型和运行类型可以不一致
(2)编译类型在定义对象时,就确定了,不能改变
(3)运行类型是可以变化的.
(4)编译类型看定义时=号的左边,运行类型看=号的右边

Animal animal = new Dog(); 【animal编译类型是Animal,运行类型Dog】

animal = new Cat();【animal的运行类型变成了Cat,编译类型仍然是 Animal)

定义Animal类

public class Animal {
    public void cry() {
        System.out.println("Animal cry()动物在叫....");
    }
}

定义Dog和Cat类继承分别Animal类

public class Dog extends Animal {
    @Override
    public void cry() {
        System.out.println("Dog cry() 小狗汪汪叫...");
    }
}
public class Cat extends Animal {
    @Override
    public void cry() {
        System.out.println("Cat cry() 小猫喵喵叫...");
    }
}

多态注意事项和细节讨论

◆ 多态的前提是:两个对象(类)存在继承关系

◆ 多态的向上转型

1)本质:父类的引用指向了子类的对象
2)语法:父类类型 引用名= new子类类型();
3)特点:编译类型看左边,运行类型看右边。可以调用父类中的所有成员(需遵守访问权限).不能调用子类中特有成员;
最终运行效果看子类的具体实现!

//向上转型:父类的引用指向了子类的对象
  //语法: 父类类型 引用名 = new 子类类型();
  Animal animal = new Cat();
  Object object = new Cat();//可以吗? 可以 Object也是 Cat 的父类

  //(1)可以调用父类中的所有成员
  //(2)不能调用子类中特有成员;
  //(3)因为在编译阶段,能调用那些成员,是由编译类型来决定的
  //animal.catchMouse();错误
  //最终运行效果看子类的具体实现
  //(4)最终运行效果看子类(运行类型)的具体实现, 即调用方法时,按照从子类(运行类型)开始查找方法
  // 然后调用,规则我前面我们讲的方法调用规则一致。
  animal.eat();//猫吃鱼..
  animal.run();//跑
  animal.show();//hello,你好
  animal.sleep();//睡

◆ 多态向下转型

1)语法:子类类型 引用名=子类类型)父类引用;
2)只能强转父类的引用,不能强转父类的对象
3)要求父类的引用必须指向的是当前目标类型的对象
4)当向下转型后,可以调用子类类型中所有的成员

//多态的向下转型
  //1)语法:子类类型引用名=(子类类型)父类引用;
  //2)只能强转父类的引用,不能强转父类的对象
  //3)要求父类的引用必须指向的是当前目标类型的对象
  //4)当向下转型后,可以调用子类类型中所有的成员
  Cat cat = (Cat) animal;
  cat.catchMouse();
  cat.eat();

instanceOf 比较操作符

instanceOf 比较操作符,用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型

public class PolyDetail03 {
    public static void main(String[] args) {
        BB bb = new BB();
        System.out.println(bb instanceof BB);// true
        System.out.println(bb instanceof AA);// true
        //aa 编译类型 AA, 运行类型是 BB
        //BB 是 AA 子类
        AA aa = new BB();
        System.out.println(aa instanceof AA);
        System.out.println(aa instanceof BB);
        Object obj = new Object();
        System.out.println(obj instanceof AA);//false
        String str = "hello";
        //System.out.println(str instanceof AA);
        System.out.println(str instanceof Object);//true
    }
}

class AA {
} //父类

class BB extends AA {
}//子类

多态的应用

1) 多态数组

数组的定义类型为父类类型,里面保存的实际元素类型为子类类型

应用实例:现有一个继承结构如下:

要求创建 1 个 Person 对象、2 个 Student 对象和 2 个Teacher 对象, 统一放在数组中,并调用每个对象say 方法. 应用实例升级:如何调用子类特有的方法,比如
Teacher 有一个 teach , Student 有一个 study
怎么调用?

定义 Person 类

public class Person {//父类
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String say() {
        return name + "\t" + age;//返回名字和年龄
    }
}

定义 Student 继承 Person 类

public class Student extends Person {
    private double score;
    public Student(String name, int age,double score) {
        super(name, age);
        this.score = score;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }
    //重写父类的say()

    @Override
    public String say() {
        return "学生 "+super.say()+" score="+score;
    }
    //特有方法
    public void study(){
        System.out.println("学生 "+ getName()+" 正在学Java课程...");
    }
}

定义 Teacher 继承 Person 类

public class Teacher extends Person{
    private double salary;
    public Teacher(String name, int age,double salary) {
        super(name, age);
        this.salary = salary;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double sal) {
        this.salary = sal;
    }
    //重写父类的say()

    @Override
    public String say() {
        return "老师 "+super.say()+" sal="+salary;
    }

    public void teach(){
        System.out.println("老师 "+ getName()+"  正在讲Java课程...");
    }
}

定义 PloyArray 进行方法调用

public class PloyArray {
    public static void main(String[] args) {
        Person[] persons = new Person[5];
        persons[0] = new Person("jack", 20);
        persons[1] = new Student("marry", 18, 100);
        persons[2] = new Student("smith", 19, 30.1);
        persons[3] = new Teacher("scott", 30, 20000);
        persons[4] = new Teacher("king", 50, 50000);

        for (int i = 0; i < persons.length; i++) {
            if (persons[i] instanceof Student){
//                Student students = (Student)persons[i];
//                students.study();
                ((Student)persons[i]).study();
            }else if (persons[i] instanceof Teacher){
                ((Teacher)persons[i]).teach();
            }else if (persons[i] instanceof Person){

            }else {
                System.out.println("你的类型有误,请自己检查");
            }
            persons[i].say();
            System.out.println(persons[i].say());
        }
    }
}

2)多态参数

方法定义的形参类型为父类类型,实参类型允许为子类类型

定义员工类Employee,包含姓名和月工资[private],以及计算年工资getAnnual的方法。

普通员工和经理继承了员工,经理类多了奖金bonus属性和管理manage方法,普通员工类多了work方法,普通员工和经理类要求分别重写getAnnual方法

测试类中添加一个方法showEmpAnnual(Employee e),实现获取任何员工对象的年工资,并在main方法中调用该方法[e.getAnnual()]

测试类中添加一个方法,testWork,如果是普通员工,则调用work方法,如果是经理,则调用manage方法

定义 Employee 类

public class Employee {
    private String name;
    private double salary;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public double getAnnual(){
        return 12 * salary;
    }
}

定义 Manager 继承 Employee

public class Manager extends Employee {
    private double bonus;

    public Manager(String name, double salary, double bonus) {
        super(name, salary);
        this.bonus = bonus;
    }

    public double getBonus() {
        return bonus;
    }

    public void setBonus(double bonus) {
        this.bonus = bonus;
    }

    public void Manager() {
        System.out.println("经理 " + getName() + " is managing");
    }

    @Override
    public double getAnnual() {
        return super.getAnnual() + bonus;
    }
}

定义 Worker 继承 Employee

public class Worker extends Employee{

    public Worker(String name, double salary) {
        super(name, salary);
    }

    public void work(){
        System.out.println("普通员工 "+getName()+" is working");
    }

    @Override
    public double getAnnual() {//因为普通员工没有其他的收入,则直接调用父类方法
        return super.getAnnual();
    }
}

定义 PloyParameter 调用方法

public class PloyParameter {
    public static void main(String[] args) {
        Worker tom = new Worker("tom", 2500);
        Manager milan = new Manager("milan", 5000, 20000);
        PloyParameter ployParameter = new PloyParameter();
        ployParameter.showEmpAnnual(tom);
        ployParameter.tetWork(tom);
        ployParameter.showEmpAnnual(milan);
        ployParameter.tetWork(milan);
    }

    public void showEmpAnnual(Employee e) {
        System.out.println(e.getAnnual());
    }

    public void tetWork(Employee e) {
        if (e instanceof Worker) { //向下转型
            ((Worker) e).work();
        } else if (e instanceof Manager) { //向下转型
            ((Manager) e).Manager();
        }else {
            System.out.println("不做处理");
        }
    }
}