java中的多态(polymorphic)

本文介绍了Java中的多态概念,包括方法的重载和重写、对象的多态(编译类型与运行类型的关系)、动态绑定机制以及多态在数组和函数参数中的应用。通过实例展示了如何提高代码复用性和灵活性。

一、为什么需要多态

//传统方法 若动物种类  和 食物种类过多  ,将会构成过多的重载,过于繁琐,不利于维护和管理

现在问题是代码的复用性不高

而使用多态可以使用我们代码的复用性

二、基本介绍

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

多态的具体体现

1、方法的多态

   方法的重写和重载就体现了多态

package com.hspedu.poly_;

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() 方法被调用...");
    }
}
2、对象的多态(核心,难点,重点)

   1)一个对象的   编译类型  可以和  运行类型   不一致 

        (new 的是 运行类型 ,定义对象名 的是  编译类型)

Animal animal = new Dog();//对象animal 编译类型是Animal,运行类型是  Dog 
//父类(Animal)的引用可以指向子类(Dog)的一个对象。

  2)编译类型在定义对象的时候,就确定了,不能改变

  3)运行类型是可以改变的

Animal animal = new Dog();//对象animal 编译类型是Animal,运行类型是  Dog
animal = new Cat();//此时animal的运行类型变成了Cat,编译类型仍然是Animal

  4)定义对象时,等号左边是编译类型,等号右边是运行类型

三、快速入门

package com.lmdedu.Poly.objectPoly;

public class PolyObject {
    public static void main(String[] args) {
        //体验对象多态特点

        //animal编译类型是Animal,运行类型是Dog
        Animal animal = new Dog();
        //因为运行时,这时即执行到该行时,animal的运行类型是Dog,所以会执行Dog类的cry()方法
        animal.cry();//狗叫

        //animal编译类型是Animal,运行类型是Cat,所以会执行Cat类的cry()方法
        animal = new Cat();//赋值后,指向发生变化
        animal.cry();

    }
}

使用多态机制解决前面提到的主人喂食问题 

    //使用多态机制,统一的管理主人喂食问题,
    //animal的编译类型是Animal,可以指向(接受)Animal子类的对象,
    //food的编译类型是Food,可以指向(接受)Food子类的对象,
    public void feed(Animal animal,Food food){
        System.out.println("主人" + name + "给" + animal.getName() + "喂食" + food.getName());
    }
    
    //传统方法
//    //完成主人给小狗喂 骨头
//    public void feed(Dog dog,Bone bone){
//        System.out.println("主人" + name + "给" + dog.getName() + "喂食" + bone.getName());
//    }
//    //主人给小猫喂 鱼
//    public void feed(Cat cat,Fish fish){
//        System.out.println("主人" + name + "给" + cat.getName() + "喂食" + fish.getName());
//    }
        //添加给小猪喂米饭
        Animal pig = new Pig("小花猪");
        Food rice = new Rice("米饭");
        System.out.println("=====================");
        tom.feed(pig, rice);

四、多态的注意事项

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

1、多态的向上转型

    1)本质:父类的引用  指向了  子类的对象

    2)语法:父类类型   引用名 = new 子类类型();   

    3)特点:编译类型看左边,运行类型看右边

                   可以调用父类中的所有成员(属性和方法)(需遵守访问权限)

                   但是不能调用子类中特有成员(属性和方法)

                   因为在编译阶段,能调用哪些成员,是由编译类型决定的

                   最终运行效果看子类的具体实现,因为在运行阶段,是由运行类型决定的

                   编译看父类,运行看子类

2、多态的向下转型

       目的:为了解决  上面说的  不能调用子类中特有成员

    1)语法  子类类型  引用名 = (子类类型) 父类引用;(将一个父类引用强制转换为子类类型               的引用)

        //老师希望可以调用Cat的catchMouse方法,因为此方法为子类的特有成员
        //于是使用多态的向下转型
        //语法 子类类型 引用名 = (子类类型) 父类引用;
        //将编译类型由  Animal强转为 Cat
        //此时编译类型为 Cat,运行类型为 Cat
        Cat cat = (Cat) animal;
        cat.catchMouse();

    2)只能强转父类的引用,不能强转父类的对象(只能改变指向的类型,不能改变存储空间数                 据)

   3)要求   父类的引用  必须指向的是   当前目标类型的对象

        

        //要求父类的引用   必须是指向的是 当前目标类型的对象
        //由上面Animal animal = new Cat();知道 animal本来就是指向 Cat的,所以可以强转为
        //但若将 一个运行类型为 Cat的 animal 强转为一个Dog,将报错ClassCastException
        Dog dog = (Dog) animal;

   4)当 向下转型后,就可以调用  子类类型的  所有成员了

3、属性没有重写之说,属性的值看编译类型
4、instanceOf比较操作符

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

用法:  对象 instanceOf 类型

Object object = new Object();
System.out.println(obj instanceOf AA);//其实就是问 obj是AA或者AA的子类吗
//因为Obj的运行类型是 Object,不是AA或者AA的子类,所以为false

五、练习

输出 20 20 true 10 20

六、java的动态绑定机制(重点)

定义

1、当调用对象的方法时,该方法会和对象的运行类型绑定(从运行类型开始找能否调用该方法,若没有就继续往上找)

2、当调用对象的属性时,没有动态绑定机制,遵守哪里声明,哪里使用

System.out.println(a.sum());

若此时将 B类中的sum()方法注销,那么a.sum()就会去找A的sum()方法,但是父类中的sum()方法有一个getI()方法,子类和父类都有getI()方法,于是就引出了 动态绑定机制

因为a的运行类型是  子类B,所以 根据动态绑定机制,于是就调用了子类B的getI()方法

因此父类中的sum()方法就是return 的 20+10

System.out.println(a.sum1());

此时注销B类中的sum1()方法,则会去调用A类的sum1()方法,return i+10;这里的 i 是属性,而属性没有动态绑定机制,哪里声明,哪里调用,因此这里的i调用的是B类的i = 10;于是10+10.

0314_韩顺平Java_动态绑定机制_哔哩哔哩_bilibili

七、多态数组

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

package com.lmdedu.Poly.PolyArray;

public class PolyArray {
    public static void main(String[] args) {
        //创建一个Person,2个Student 2个 Teacher,统一放在数组中,并调用每个对象的say()方法
        //创建一个Person类型的数组
        Person[] person = new Person[5];
        person[0] = new Person("jack",20);
        person[1] = new Student("marry",20,100);
        person[2] = new Student("smith",19,30.1);
        person[3] = new Teacher("scott",30,11000);
        person[4] = new Teacher("king",50,21000);


        //循环遍历多态数组,调用say()
        for(int i = 0; i < person.length; i++){
            //person[i],编译类型为Person,运行类型根绝实际情况由jvm机来判断
            System.out.println(person[i].say());;//动态绑定机制

            //调用子类特有的方法
            if(person[i] instanceof Student){//instanceof用于判断运行类型
                //(Student)person[i]将person[i]强转为Student
                Student student = (Student)person[i];
                (student).study();
                //或者简洁一点((Student)person[i]).study();
            } else if (person[i] instanceof Teacher) {
                //(Teacher)person[i]将person[i]强转为Teacher类型
                ((Teacher)person[i]).teach();
            }else if(person[i] instanceof Person){

            }else{
                System.out.println("你的类型有误");
            }
        }
    }
}

八、多态参数

1、比如前面的主人喂食物问题,feed函数中 第一个参数可以为 Animal,也可以为Animal的子类Dog,Cat,第二个参数可以为Food,也可以为Food的子类 Bone,rice等

//调用子类的特有方法  需要进行强转,向下转型  ((子类类型)对象).调用的子类特有方法
package com.lmdedu.Poly.poly_parameter;

public class PolyParameter {
    public static void main(String[] args) {
        Worker tom = new Worker("tom",2500);
        manager jack = new manager("jack", 5000,1100);

        Test test = new Test();

        test.showEmpAnnual(tom);
        test.showEmpAnnual(jack);

        test.testWork(tom);
        test.testWork(jack);
    }
}

class Test{
    public void showEmpAnnual(Employee e){
         //会动态绑定,传进来的e是什么类,就会调用对应的getAnnual()
        System.out.println(e.getAnnual());
    }
    //调用子类的特有方法  需要进行强转,向下转型  ((子类类型)对象).调用的子类特有方法
    //若为普通员工就调用work方法,若为经理, 则调用manager方法
    public void testWork(Employee e){
        if(e instanceof Worker){
            //将编译类型强转为Worker
            ((Worker)e).work();
        }else if(e instanceof manager){
            ((manager)e).manage();
        }else{
            System.out.println("输入类型有误");
        }
    }

}
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;
    }
}

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 manage(){
        System.out.println("经理" + getName() + "正在管理");
    }
    //重写获取年薪的方法
    @Override
    public double getAnnual() {
        return super.getAnnual() + bonus;
    }
}

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

    public void work(){
        System.out.println("普通员工" + getName() + "正在工作");
    }

    //重写获取年薪的方法
    @Override
    public double getAnnual(){
        //普通员工没有奖金,所以直接继承getAnnual()方法
        return super.getAnnual();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值