面向对象三大特征——多态

目录

基本介绍

多态的注意事项和使用细节

多态的向上转型

 多态的向下转型

多态例题

思路分析:

动态绑定机制

多态数组

多态参数


多态的是什么

什么是多态 。多就是[多种]态[状态],我们成为多态,可能这样看起来比较抽象,但是没事我们继续往下看

基本介绍

方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上。一讲到多态,肯定会有运行类型和编译类型,那么什么是编译类型什么是运行呢,有这么一句话=编译类型看左边运行类型看右边

来看看具体的案例吧

Dog dog = new Animal();

上面的是一个非常简单的代码,那么根据上的的这样一句话,编译类型看等号的左边,运行类型看等号的右边,因此上面的这一句代码的

编译类型是:Dog

运行类型是:Animal

那么这个有什么作用呢,非常的重要,接下来马上就会讲到

多态的注意事项和使用细节

多态的向上转型

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

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

3)特点:编译类型看左边,运行类型看右边。 可以调用父类中的所有成员(需遵守访问权限),不能调用子类中特有成员; 最终运行效果看子类的具体实现!

向上转型调用方法的规则如下:
(1)可以调用父类中的所有成员(需遵守访问权限)
(2)但是不能调用子类的特有的成员
(#)因为在编译阶段,能调用哪些成员,是由编译类型来决定的
animal.catchMouse();错误
(4)最终运行效果看子类(运行类型)的具体实现, 即调用方法时,按照从子类(运行类型)开始查找方法,然后调用,规则我前面我们讲的方法调用规则一致。

 多态的向下转型

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

2)只能强转父类的引用,不能强转父类的对象

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

4)当向下转型后,可以调用子类类型中所有的成员(也要遵守访问权限)

//多态的向下转型
//(1)语法:子类类型 引用名 =(子类类型)父类引用;

向上转型和向下转型的代码演示:

package idea.polymorphic.detail;

/**
 * 演示向上转型
 */
public class PolyDetail01 {
    public static void main(String[] args) {
        //向上转型: 父类的引用指向了子类的对象
        //语法:父类类型 引用名 = new 子类类型();

        /*
        这就是一个向上转型
        此时我们看,Animal animal = new Cat(); 这句话的编译类型和运行类型是什么
        还记得我们的话嘛,编译类型看=左边 运行类型看=右边
        编译类型是 Animal
        运行类型是 Cat
        语法:父类类型 引用名 = new 子类类型();
         */
        Animal animal = new Cat();
        Object obj = new Cat();//这样也是可以的,因为Cat是Animal的子类,而Animal是Object的子类,因此这样也是满足向上转型的语法

        //向上转型调用方法的规则如下:
        //(1)可以调用父类中的所有成员(需遵守访问权限)
        //可以调用父类中的属性,但是私有的属性不能够访问
        System.out.println(animal.name);
        System.out.println(animal.age);
        //System.out.println(animal.id);因为id属性是private的,因此不能直接访问会报错

        //看看方法  都可以调用父类中的方法(需要遵守访问权限,如果是私有的方法也不能访问,和调用属性的规则一样)
        animal.eat();
        animal.run();
        animal.show();
        animal.sleep();

        //(2)但是不能调用子类的特有的成员
        //(#)因为在编译阶段,能调用哪些成员,是由编译类型来决定的
        //因为这是子类特有的方法,所以不能够去访问
        //animal.catchMouse();//错误的

        //(4)最终运行效果看子类(运行类型)的具体实现, 即调用方法时,按照从子类(运行类型)开始查找方法,然后调用,规则我前面我们讲的方法调用规则一致。
        //我们知道,在子类中也有一个eat()方法,该方法重写了父类的eat()方法,那么在使用animal去调用eat方法的时候,到底输出的是父类的方法呢,还是子类的方法
        //结果是子类的方法,因为最终运行起来,最后看的还是运行类型,而我们此时的运行类型是cat,那么在当程序运行起来
        animal.eat();


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

        //只能强转父类的引用,不能强转父类的对象 这句话怎么理解我们看上面的这个代码    Animal animal = new Cat();
        //animal就是父类的引用,因为Animal是父类
        //所以向下转型就是 把animal强装成一个Cat
        Cat cat = (Cat) animal;
        //此时我们再来看编译类型和运行类型
        //编译类型是 Cat
        //运行类型是 Cat

        //向下转型后就可以去访问访问子类中的所以属性,但是也要遵守访问权限
        cat.catchMouse();//这样就不会报错了



    }
}

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

代码演示:

package idea.polymorphic.detail;

public class PolyDetail02 {
    public static void main(String[] args) {
        //属性没有重写之说!属性的值看编译类型

        //向上转型
        Base base = new son();
        /*
        这里输出的就是10,因为属性没有重写之说
        属性的值看编译类型  此时的编译类型是 Base  运行类型是 son
        所以此时输出的count就是10
         */
        System.out.println(base.count);

        son son = new son();
        /*
        这里输出的就是20,因为属性没有重写之说
        属性的值编译类型  此时的编译类型是 Base  运行类型是 son
        所以此时输出的count就是20
         */
        System.out.println(son.count);
    }
}

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

class son extends Base {//子类
    int count = 20;
}

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

代码演示:

package idea.polymorphic.detail;

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();
        //看的是运行类型 此时aa的运行类型是BB 而BB是AA的子类,所以他们是同一个对象
        System.out.println(aa instanceof AA);
        //看的是运行类型 此时aa的运行类型是BB 而BB就是BB,所以他们是同一个对象
        System.out.println(aa instanceof BB);

        //obj 编译类型 Object , 运行类型是 Object
        Object obj = new Object();
        //看的是运行类型 此时obj的运行类型是Object 而obj不是AA的子类,所以他们不是同一个对象
        System.out.println(obj instanceof AA);//false
        String str = "hello";
        //System.out.println(str instanceof AA);
        //因为String是Object的子类所以返回true
        System.out.println(str instanceof Object);//true
    }
}

class AA {

}

class BB extends AA {

}

多态例题

思路分析:

我们要知道对于属性来说没有重写的概念,因此在多态中属性看的是。编译类型,而方法看的是运行类型,因此我们只需要分清楚一个对象实例的编译类型和运行类型,那么就不容易出错,在本例题中我们可以清楚的感受到编译类型和运行类型,仔细看代码中的注释,就可以清楚的知道为什么输出这样值

package idea.polymorphic;

/**
 * 讲解多态的例题
 */
public class polyExercise01 {
    public static void main(String[] args) {
        
        Sub s = new Sub();
        //s的编译类型是 Sub  运行类型是 Sub  因为属性看的是编译类型所以s.count输出的就是20
        System.out.println(s.count);//20
        //方法看的是运行类型 所以调用的是Sub中的display方法 因为是this.count 所以访问的就是本类中的属性
        s.display();//20
        
        Base b = s;
        //因为把s赋给了 b 此时 编译类型变成了 Base  运行类型变成了Sub
        System.out.println(b == s);//T  把s赋给了b 相当于 b 和 s 都指向同一块空见,所以返回true
        //属性看的是编译类型,此时编译b的编译类型是Base 所以b.count 输出的就是10
        System.out.println(b.count);//10
        //方法看的是运行类型 因为b的运行类型是 Sub 所以b.display()调用的还是Sub中的方法,因此输出的是20
        b.display();//20
    }
}

class Base {//父类
    int count = 10;

    public void display() {
        System.out.println(this.count);
    }
}

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

    public void display() {
        System.out.println(this.count);
    }
}

动态绑定机制

1.当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定

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

        System.out.println(a.sum());//?40 -> 30
        /*
        思路分析
        1.首先我们知道方法看的是运行类型,而a的运行类型是B 所以会去B类中去找sum()方法,但是B类中没有sum()方法,因为B类继承了A类,所以
          会去A类中查找,在A类中我们确实查找到了sum()方法,
        2.在A类的sum()方法中,有这么一句话return getI()+10; ,当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定,因此
          调用这个方法的是a,但是a的编译类型是B,因此会进行动态绑定,会先去B类中查找getI()方法,如果在B类中查找不到,才回去A类查找
        3.我们在B类中找到了getI()方法,里面有一句return i; 因为在调用属性时,没有动态绑定机制,所以返回的就是B类中的i,因此返回的就是20
          如果在B类中,没有i,那么就会到A类也就是父类中去查找
        4.因此最后a.sum(); 输出的就是30
         */

        System.out.println(a.sum1());//?30-> 20
        /*
        思路分析
        1.首先我们知道方法看的是运行类型,而a的运行类型是B 所以会去B类中去找sum1()方法,但是B类中没有sum1()方法,因为B类继承了A类,所以
          会去A类中查找,在A类中我们确实查找到了sum1()方法,
        2.在A类的sum1()方法中,有这么一句话return i + 10; ,因为在调用对象属性的时候,没有动态绑定机制,哪里声明哪里使用,所以这里的i
          时父类中的i 也就是A类中的i 因此a.sum1()方法输出的就是20
        3.如果一直找不到i,最后会报错
         */

package idea.polymorphic.DynamicBindingMechanism;

/**
 * 讲解动态绑定机制
 */
public class DynamicBinding {
    public static void main(String[] args) {
        //a 的编译类型 A, 运行类型 B
        A a = new B();//向上转型
        //结论
        //1.当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
        //2.当调用对象属性时 ,没有动态绑定机制,哪里声明哪里使用

        System.out.println(a.sum());//?40 -> 30
        /*
        思路分析
        1.首先我们知道方法看的是运行类型,而a的运行类型是B 所以会去B类中去找sum()方法,但是B类中没有sum()方法,因为B类继承了A类,所以
          会去A类中查找,在A类中我们确实查找到了sum()方法,
        2.在A类的sum()方法中,有这么一句话return getI()+10; ,当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定,因此
          调用这个方法的是a,但是a的编译类型是B,因此会进行动态绑定,会先去B类中查找getI()方法,如果在B类中查找不到,才回去A类查找
        3.我们在B类中找到了getI()方法,里面有一句return i; 因为在调用属性时,没有动态绑定机制,所以返回的就是B类中的i,因此返回的就是20
          如果在B类中,没有i,那么就会到A类也就是父类中去查找
        4.因此最后a.sum(); 输出的就是30
         */

        System.out.println(a.sum1());//?30-> 20
        /*
        思路分析
        1.首先我们知道方法看的是运行类型,而a的运行类型是B 所以会去B类中去找sum1()方法,但是B类中没有sum1()方法,因为B类继承了A类,所以
          会去A类中查找,在A类中我们确实查找到了sum1()方法,
        2.在A类的sum1()方法中,有这么一句话return i + 10; ,因为在调用对象属性的时候,没有动态绑定机制,哪里声明哪里使用,所以这里的i
          时父类中的i 也就是A类中的i 因此a.sum1()方法输出的就是20
        3.如果一直找不到i,最后会报错         
         */
        
    }
}

class A {//父类
    public int i = 10;
    //动态绑定机制:

    public int sum() {//父类sum()
        return getI() + 10;//20 + 10
    }

    public int sum1() {//父类sum1()
        return i + 10;//10 + 10
    }

    public int getI() {//父类getI
        return i;
    }
}

class B extends A {//子类
    public int i = 20;

//    public int sum() {
//        return i + 20;
//    }

    public int getI() {//子类getI()
        return i;
    }

//    public int sum1() {
//        return i + 10;
//    }
}

多态数组

数组体现多态,在下面的代码中我们创建了一个Person数组,因为数组也可以体现多态,因为Teacher类和Student都继承了Person类,所以Student和Teacher都可以存在在Person数组中

代码演示:

package idea.polymorphic.polyarr;

/**
 * 演示数组体现多态
 */
public class PolyArray {
    public static void main(String[] args) {
        //创建了一个Person数组,因为数组也可以体现多态,因为Teacher类和Student都继承了Person类,所以Student和Teacher都可以存在在Person数组中
        Person[] p = new Person[5];
        //初始化数组的值,数组中的类型各不相同,有Person类型的,Student类型的,Teacher类型的
        p[0] = new Person("jack", 10);
        p[1] = new Student("tom", 10, 100);
        p[2] = new Student("many", 10, 90);
        p[3] = new Teacher("A", 10, 10000);
        p[4] = new Teacher("B", 10, 20000);

        //遍历这个数组
        for (int i = 0; i < p.length; i++) {
            //调用数组中每一个元素的say()方法,因为有动态绑定机制的存在,在调用方法时,看的是运行类型,因此会自动的找到,对应类中的方法去调用
            System.out.println(p[i].say());
            if (p[i] instanceof Student) {//判断数组中的每个元素的运行类型是声明,如果是Student 那么就强转p[i]然后去调用Student类的方法,相当于向下转型
                ((Student) (p[i])).study();
            } else if (p[i] instanceof Teacher) {//判断数组中的每个元素的运行类型是声明,如果是Teacher 那么就强转p[i]然后去调用Teacher对应类的方法,相当于向下转型
                ((Teacher) p[i]).teach();
            } else {
                System.out.println("不做处理");
            }
        }


    }
}


Person类 

package idea.polymorphic.polyarr;

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类

package idea.polymorphic.polyarr;

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类 

package idea.polymorphic.polyarr;

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 salary) {
        this.salary = salary;
    }
    //写重写父类的say方法

    @Override
    public String say() {
        return "老师 " + super.say() + " salary=" + salary;
    }
    //特有方法
    public void teach() {
        System.out.println("老师 " + getName() + " 正在讲java课程...");
    }
}

多态参数

思路分析:

test(jack);
test(tom);

        1.test()方法可以接受一个Employee对象,因为Worker和Manage都是Employee的子类,所以可以接受参数
        2.因为在调用方法的时候,看的是运行类型,因此我们传入一个worker对象,那么就会动态绑定到Worker类中的getAnnual()方法
          如果传入的是,Manager,那么就会动态绑定到Manager中

思路分析:

testWork(jack);
testWork(tom);

        1.testWork()方法可以接受一个Employee对象,因为Worker和Manage都是Employee的子类,所以可以接受参数
        2.这次我们要调用的是子类中的特有属性,因此要对传入的参数进行判断,如果e的类型是Worker,那么就强转,相当于向下转型,并且会去调用对应的方法

package idea.polymorphic.polyparameter_;

/**
 * 演示参数体现多态
 */
public class PloyParameter {
    public static void main(String[] args) {
        //这里我们创建了一个Worker对象
        Worker jack = new Worker("jack", 1000);
        //这里我们创建了一个Manager对象
        Manager tom = new Manager("tom", 2000, 1000);

        /*
        思路分析
        1.test()方法可以接受一个Employee对象,因为Worker和Manage都是Employee的子类,所以可以接受参数
        2.因为在调用方法的时候,看的是运行类型,因此我们传入一个worker对象,那么就会动态绑定到Worker类中的getAnnual()方法
          如果传入的是,Manager,那么就会动态绑定到Manager中
         */
        test(jack);
        test(tom);

        /*
        思路分析
        1.testWork()方法可以接受一个Employee对象,因为Worker和Manage都是Employee的子类,所以可以接受参数
        2.这次我们要调用的是子类中的特有属性,因此要对传入的参数进行判断,如果e的类型是Worker,那么就强转,相当于向下转型,并且会去调用对应的方法
         */
        testWork(jack);
        testWork(tom);

    }

    //该方法可以接受一个Employee 对象
    public static void test(Employee e) {
        System.out.println(e.getAnnual());
    }

    //该方法可以接受一个Employee 对象
    public static void testWork(Employee e) {
        if (e instanceof Worker) {
            ((Worker) e).work();
        } else if (e instanceof Manager) {
            ((Manager) e).manage();
        } else {
            System.out.println("不做任何处理 ");
        }
    }
}

employee类

package idea.polymorphic.polyparameter_;

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

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

    //得到年工资的方法
    public double getAnnual() {
        return 12 * 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;
    }
}

Manager类

package idea.polymorphic.polyparameter_;

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 manage() {
        System.out.println("经理 " + getName() + " is managing");
    }

    //重写获取年薪方法
    @Override
    public double getAnnual() {
        return super.getAnnual() + bonus;
    }
}

Worker类

package idea.polymorphic.polyparameter_;

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值