java----面向对象之多态

问:

什么是多态,形成多态的条件是什么?

引入

多态是继封装、继承之后,面向对象的第三大特性。

生活中,比如跑的动作,小猫、小狗和大象,跑起来是不一样的。再比如飞的动作,昆虫、鸟类和飞机,飞起来也是不一样的。可见,同一行为,通过不同的事物,可以体现出来的不同的形态。多态,描述的就是这样的状态。

定义
  • 多态: 是指同一行为,对于不同的对象具有多个不同表现形式。
  • 程序中多态: 是指同一方法,对于不同的对象具有不同的实现.
  • 例如: 吃饭
  • 狗: 吃骨头
  • 猫:猫吃鱼
前提条件【重点】
  1. 继承或者实现【二选一】
  2. 父类引用指向子类对象【格式体现】  Animal anl = new Dog(); A a = new Imp(😉
  3. 方法的重写【意义体现:不重写,无意义】

实现多态

多态的体现:父类的引用指向它的子类的对象

父类类型 变量名 = new 子类对象;
变量名.方法名();

父类类型:指子类对象继承的父类类型,或者实现的父接口类型。

class Animal{}
class Cat extends Animal{}
class Dog extends Animal{}
clsss Person{}
//测试类:
main(){
    Animal a1 = new Cat();
    Animal a2 = new Dog();
    Animal a3 = new Person();//编译错误,没有继承关系。
}

多态的表现形式:

  • 普通父类多态

    public class Fu{}
    public class Zi extends Fu{}
    public class Demo{
    public static void main(String[] args){
    Fu f = new Zi();//左边是一个“父类”
    }
    }

    
    
  • 抽象父类多态

    public abstract class Fu{}
    public class Zi extends Fu{}
    public class Demo{
        public static void main(String[] args){
            Fu f = new Zi();//左边是一个“父类”
        }
    }
    
    
  • 父接口多态

public interface A{}
public class AImp implements A{}
public class Demo{
    public static void main(String[] args){
        A a = new AImp();
    }
}

多态时访问成员的特点—重点

  • 多态时成员变量的访问特点
    • 编译看左边,运行看左边
      • 简而言之:多态的情况下,访问的是父类的成员变量
  • 多态时成员方法的访问特点
    • 非静态方法:编译看左边,运行看右边
      • 简而言之:编译的时候去父类中查找方法,运行的时候去子类中查找方法来执行
    • 静态方法:编译看左边,运行看左边
      • 简而言之:编译的时候去父类中查找方法,运行的时候去父类中查找方法来执行
  • 注意:多态的情况下是无法访问子类独有的方法
  • 演示代码:
class Fu{
    int a = 10;

    public void show1(){
        System.out.println("Fu show1...");
    }

    public static void show2(){
        System.out.println("Fu show2...");
    }
}

class Zi extends Fu{
    int a = 100;

    public void show1(){
        System.out.println("Zi show1...");
    }

    public static void show2(){
        System.out.println("Zi show2...");
    }
}

public class Test1 {
    public static void main(String[] args) {
        /*
            - 多态时成员变量的访问特点: 编译看父类,运行看父类(编译看左边,运行看左边)
            - 多态时成员方法的访问特点:
                非静态方法: 编译看父类,运行看子类(编译看左边,运行看右边)
                静态方法:   编译看父类,运行看父类(编译看左边,运行看左边)

            记忆:
                除了非静态方法是编译看父类,运行看子类,其余都是看父类
                除了非静态方法是编译看左边,运行看右边,其余都是看左边
         */
        // 父类的引用指向子类的对象
        Fu f = new Zi();
        // f访问变量a
        System.out.println(f.a);// 10
        // f访问show1方法
        f.show1();
        // f访问show2方法
        f.show2();
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wMCwF1KF-1595463944696)(img\image-20200710121035511.png)]

小结:

 多态时成员变量的访问特点: 编译看父类,运行看父类(编译看左边,运行看左边)
        多态时成员方法的访问特点:
            非静态方法: 编译看父类,运行看子类(编译看左边,运行看右边)
            静态方法:   编译看父类,运行看父类(编译看左边,运行看左边)
            
       记忆:
            除了非静态方法是编译看父类,运行看子类,其余都是看父类
            除了非静态方法是编译看左边,运行看右边,其余都是看左边
       注意: 多态的情况下,是无法访问子类独有的成员的(后期引用类型转换能解决这个弊端)

多态的应用场景:

多态的几种应用场景:

  • 变量多态 -----> 意义不大

    class Fu{
        void show(){
            System.out.println("Fu show");
        }
    }
    class Zi extends Fu{
        void show(){
            System.out.println("Zi show");
        }
    
        void method(){
            System.out.println("Zi method");
        }
    }
    public class Test1 {
        public static void main(String[] args) {
            /*
                - 变量多态 -----> 意义不大
             */
            Fu f = new Zi();
            f.show();
            // f.method();// 编译报错,因为多态成员访问,编译看父类,而父类没有这个方法
    
            Zi zi = new Zi();
            zi.show();
            zi.method();
    
        }
    }
    
  • 形参多态----> 常用

    abstract class Animal{
        public abstract void eat();
    }
    
    class Dog extends Animal{
        @Override
        public void eat() {
            System.out.println("狗吃骨头...");
        }
    }
    
    class Cat extends Animal{
        @Override
        public void eat() {
            System.out.println("猫吃鱼...");
        }
    }
    public class Test2 {
        public static void main(String[] args) {
            /*
                形参多态----> 常用
             */
            Dog dog = new Dog();
            invokeEat(dog);
    
            System.out.println("-------------------------------------");
    
            Cat cat = new Cat();
            invokeEat(cat);
    
            // Animal anl = dog;  ===> Animal anl = new Dog();
            // anl = cat;  ===> anl = new Cat();
        }
    
        // 定义一个方法,可以用来接收Dog和Cat对象,并在该方法中调用他们的eat方法
        /*public static void invokeEat(Dog d){
            d.eat();
        }
    
        public static void invokeEat(Cat c){
            c.eat();
        }*/
        // 问题:如果Animal类有100个子类,都需要定义这种方法来调用一下eat方法,那么就需要定义100个重载的方法来实现
        //      出现了代码冗余,而且可扩展性不强,后期维护成本很大
        // 解决: 形参多态
        // 原因: 父类类型的变量作为方法的形参,就可以接受该父类所有的子类对象
        public static void invokeEat(Animal anl){
            anl.eat();
        }
    
    
    }
    
  • 返回值多态—> 常用

    class Person{
    
    }
    
    class Student extends Person{
    
    }
    
    class Teacher extends Person{
    
    }
    
    public class Test3 {
        public static void main(String[] args) {
            /*
                返回值多态---> 常用
             */
            Person p = getObject();
        }
    
        // 结论: 返回值类型为父类类型,可以返回该父类的所有子类对象
        public static Person getObject(){
            if (1==1){
                return new Student();
            }else {
                return new Teacher();
            }
        }
    
        public static Person getObject(Person p){
            // ...
           return p;
        }
    }
    

小结:

  • 变量多态— 不常见 集合阶段: List list = new ArrayList();
  • 形参多态— 父类类型作为方法的参数,就可以接收该父类类型的所有子类对象
  • 返回值多态—父类类型作为方法的返回值类型,就可以返回该父类类型的所有子类对象
  • 问题:
    • 如果参数类型为Object类型,那么就可以接收所有类的对象
    • 如果返回值类型为Object类型,那么就可以返回所有类的对象

多态的好处和弊端

  • 好处
    • 提高了代码的扩展性
  • 弊端
    • 多态的情况下,只能调用父类的共性内容,不能调用子类的特有内容。
  • 示例代码
多态的好处:===============
abstract class Animal{
    public abstract void eat();
}

class Dog extends Animal{
    @Override
    public void eat() {
        System.out.println("狗吃骨头...");
    }
}

class Cat extends Animal{
    @Override
    public void eat() {
        System.out.println("猫吃鱼...");
    }
}

public class Test1 {
    public static void main(String[] args) {
        /*
            多态的好处: 提高代码的扩展性,父类类型作为方法的参数,可以接收该父类类型所有子类对象
         */
        Dog dog = new Dog();
        invokeEat(dog);

        System.out.println("========================");

        Cat cat = new Cat();
        invokeEat(cat);


    }

    // 父类类型作为方法的参数,可以接收该父类类型所有子类对象
    public static void invokeEat(Animal anl){
        anl.eat();
    }
}


多态的弊端============
   abstract class Animal{
    public abstract void eat();
}

class Dog extends Animal{
    @Override
    public void eat() {
        System.out.println("狗吃骨头...");
    }

    // 独有功能
    public void lookHome(){
        System.out.println("狗正在看家...");
    }
}

class Cat extends Animal{
    @Override
    public void eat() {
        System.out.println("猫吃鱼...");
    }
    // 独有功能
    public void catchMouse(){
        System.out.println("猫抓老鼠...");
    }
}

public class Test2 {
    public static void main(String[] args) {
        // 多态的弊端: 无法访问子类独有的功能(成员)
        Animal anl1 = new Dog();
        anl1.eat();
        // anl1.lookHome();// 编译报错,编译看父类(左边)

        Animal anl2 = new Cat();
        anl2.eat();
        // anl2.catchMouse();// 编译报错,编译看父类(左边)

        System.out.println("==============================");
        invokeEat(anl1);// 传入Dog类对象

        invokeEat(anl2);// 传入Cat类对象
    }

    // 父类类型作为方法的参数,可以接收该父类类型所有子类对象
    public static void invokeEat(Animal anl){
        anl.eat();
        // 访问传入的子类对象的独有功能,就访问不了了
        //anl.lookHome();// 编译报错,编译看父类(左边)
        //anl.catchMouse();// 编译报错,编译看父类(左边)
    }
}

引用类型转换

向上转型
  • 子类类型向父类类型向上转换的过程,这个过程是默认的。

     Aniaml anl = new Cat();  
    
向下转型
  • 父类类型向子类类型向下转换的过程,这个过程是强制的。

     Aniaml anl = new Cat();  
     Cat c = (Cat)anl;//向下转型
     c.catchMouse();// 可以访问 子类独有的功能,解决多态的弊端
    
    
instanceof关键字
  • 向下强转有风险,最好在转换前做一个验证 :

  • 格式:

    变量名 instanceof 数据类型 
    如果变量属于该数据类型,返回true。
    如果变量不属于该数据类型,返回falseif( anl instanceof Cat){//判断anl是否能转换为Cat类型,如果可以返回:true,否则返回:false
        Cat c = (Cat)anl;//安全转换
    }
    
class Fu{

}

class Zi extends Fu{

}

class Z extends Fu{

}

public class Test1 {
    public static void main(String[] args) {
        /*
             引用类型转换: 前提条件--要是父子关系,或者实现关系
                向上转型:
                    概述: 子类类型 向 父类类型转换的一个过程,这个过程是自动的
                    格式: 父类类型 变量名 = 子类对象;

                向下转型:
                    概述: 父类类型 向 子类类型转换的一个过程,这个过程是强制的
                    格式: 子类类型 变量名 = (子类类型)父类类型的变量;
                    注意: 向下转型要求,父类类型的变量指向的对象一定要属于要转换的子类类型

               instanceof关键字:
                    作用: 可以进行类型判断
                    格式: if(变量 instanceof 数据类型){}
                    执行:
                        判断变量指向的对象是否属于instanceof后面指定的数据类型:
                            如果是属于后面指定的数据类型,那么就返回true
                            如果不属于后面指定的数据类型,那么就返回false
         */
        Zi z = new Zi();// 不转型
        // 向上转型
        Fu f = new Zi();  // 类似: double numD = 10;
        // Test1 t = new Zi();// 编译报错,因为Test1和Zi没有父子类关系

        // 向下转型
         f = new Z();
        if (f instanceof Zi){
            Zi zi = (Zi)f;
        }
        //Zi zi = (Zi)new Fu();// 运行报错,发生类型转换异常
    }
}

小结

  • 向上转型:
    	概述: 子类类型 向 父类类型转换的一个过程,这个过程是自动的
        格式: 父类类型 变量名 = 子类对象;
    
    向下转型:
    	概述: 父类类型 向 子类类型转换的一个过程,这个过程是强制的
        格式: 子类类型 变量名 = (子类类型)父类类型的变量;
    	注意: 向下转型要求,父类类型的变量指向的对象一定要属于要转换的子类类型
    
    instanceof关键字:
    	作用: 可以进行类型判断
        格式: if(变量 instanceof 数据类型){}
        执行:
       		 判断变量指向的对象是否属于instanceof后面指定的数据类型:
        		如果是属于后面指定的数据类型,那么就返回true
            	如果不属于后面指定的数据类型,那么就返回false
    

扩展多态的弊端解决

abstract class Animal{
    public abstract void eat();
}

class Dog extends Animal{
    @Override
    public void eat() {
        System.out.println("狗吃骨头...");
    }

    // 独有功能
    public void lookHome(){
        System.out.println("狗正在看家...");
    }
}

class Cat extends Animal{
    @Override
    public void eat() {
        System.out.println("猫吃鱼...");
    }
    // 独有功能
    public void catchMouse(){
        System.out.println("猫抓老鼠...");
    }
}

public class Test {
    public static void main(String[] args) {
        // 多态的弊端: 无法访问子类独有的功能(成员)
        Animal anl1 = new Dog();
        anl1.eat();
        Dog d = (Dog)anl1;
        d.lookHome();// 编译报错,编译看父类(左边)

        System.out.println("=======================================");

        Animal anl2 = new Cat();
        anl2.eat();
        Cat c = (Cat)anl2;
        c.catchMouse();// 编译报错,编译看父类(左边)

        System.out.println("=======================================");
        Dog dog = new Dog();
        invoke(dog);

        System.out.println("=======================================");
        Cat cat = new Cat();
        invoke(cat);
    }

    // 定义一个方法,可以接受Animal类的所有子类对象,在方法中调用共有的eat方法以及各子类独有的方法
    public static void invoke(Animal anl){
        anl.eat();
        // 访问传入的子类对象的独有功能,就访问不了了
        if (anl instanceof Dog){
            Dog d = (Dog)anl;
            d.lookHome();
        }
        if (anl instanceof Cat){
            Cat c = (Cat)anl;
            c.catchMouse();
        }
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值