• 【Java基础篇】第十三章 方法的覆盖和多态


    ⭐⭐⭐Java基础篇目录⭐⭐⭐


    🍁 第一章 Java开发环境搭建
    🍁 第二章 标识符与关键字
    🍁 第三章 变量
    🍁 第四章 数据类型
    🍁 第五章 运算符
    🍁 第六章 控制语句
    🍁 第七章 方法
    🍁 第八章 认识面向对象
    🍁 第九章 对象的创建和使用
    🍁 第十章 封装
    🍁 第十一章 this和static
    🍁 第十二章 继承
    🍁 第十三章 方法的覆盖和多态
    🍁 第十四章 super

    在这里插入图片描述

    1、 方法的覆盖(Override)

    子类继承父类后,当继承过来的方法无法满足当前子类的业务需求时,子类有权利对这个方法进行重新编写,有必要进行“方法的覆盖”

    😉
    方法的覆盖又称方法的重写。


    注意区分方法的重载:

    方法的重载是一个类中。方法的功能相似的话,建议将方法名定义成一样的。

    重载的构成条件是:

    • 在同一个类中
    • 方法名相同
    • 参数列表不同(参数个数、参数类型、个别的参数顺序)

    在这里插入图片描述

    2、方法覆盖的构成条件

    当子类对从父类继承过来的方法进行“方法覆盖”以后,子类对象调用该方法的时候,一定是执行覆盖之后的方法。

    方法覆盖的构成条件:

    • 两个类必须有继承关系
    • 重写之后的方法和之前的方法具有相同的返回值类型(后续有变)、相同的方法名、相同的形式参数列表
    • 访问权限不能更低,可以更高,如protected到public
    • 重写之后的方法不能比之前的方法抛出更多的异常

    在这里插入图片描述
    在这里插入图片描述

    public class OverrideTest {
        public static void main(String[] args){
            Cats c = new Cats();
            Birds b = new Birds();
            c.move();
            b.move();
            b.move(1);
    
        }
    }
    class Animals{
        public void move(){
            System.out.println("动物在移动");
        }
    }
    class Cats extends Animals{
        public void move(){
            System.out.println("走猫步");
        }
    }
    
    class Birds extends Animals{
        //形参列表不同,这里不构成覆盖
        //相比继承的无参的方法,这里构成重载
        public void move(int i){
            System.out.println("鸟在飞");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    运行结果:
    在这里插入图片描述

    在这里插入图片描述

    3、覆盖的注意事项

    • 覆盖是方法的覆盖,只针对于方法,和属性无关
    • 私有方法无法覆盖
    • 构造方法不能被继承,所以构造方法也无法被覆盖(所以尽管父类中有有参构造方法,子类对象也调用不了,但大家都有默认的无参构造方法)
    • 方法的覆盖只针对“实例方法”,静态方法的覆盖没有意义

    在这里插入图片描述
    toString()方法的输出是一个Java对象的内存地址,所以大多数时候可能需要覆盖,从而在System.out.println(引用);时,可以输出对象直观的信息,而非内存地址。

    public class TestOverride {
        public static void main(String[] args) {
            MyDate date = new MyDate();
            System.out.println(date);
    
        }
    }
    
    class MyDate{
        private int year;
        private int month;
        private int day;
    
        public int getYear(){
            return this.year;
        }
    
        public void setTear(int year){
            this.year = year;
        }
    
        public int getMonth(){
            return this.month;
        }
    
        public void setMonth(int month){
            this.month = month;
        }
    
        public int getDay(){
            return this.day;
        }
    
        public void setDay(int day){
            this.day = day;
        }
    
        //this在构造方法中的用法
        public MyDate(){
            this(2022,10,27);
        }
    
        public MyDate(int year,int month, int day){
            this.year = year;
            this.month = month;
            this.day = day;
    
        }
    
        //覆盖默认继承类Object中的toString()方法
        public String toString(){
            //同一个类中,this省了
            return year+"年"+month+"月"+day+"日";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    toString()方法覆盖以后,不再输出对象的内存地址,输出了清晰了对象的信息
    在这里插入图片描述
    😉

    4、多态

    父类型的引用指向子类型的对象,在编译阶段绑定的是父类型的方法, 在运行阶段绑定的是子类型对象的方法,即多种形态。

    在这里插入图片描述
    在这里插入图片描述

    public class Test {
        public static void main(String[] args){
            Animals a = new Animals();
            Birds b = new Birds();
            Cats c = new Cats();
            a.move();
            c.move();
            b.move();
            System.out.println("多态运行结果:");
            //多态
            //父类型的引用指向子类型的对象
            Animals a2 = new Birds();
            Animals a3 = new Cats();
            //无继承关系,error
            //Animals a4 = new Dog();
            a2.move();
            a3.move();
    
    
    
        }
    }
    class Animals{
        public void move(){
            System.out.println("动物在移动");
        }
    }
    class Cats extends Animals{
        public void move(){
            System.out.println("走猫步");
        }
    }
    
    class Birds extends Animals{
        public void move(){
            System.out.println("鸟在飞");
        }
    }
    
    class Dog{
    
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    在这里插入图片描述

    运行结果:
    在这里插入图片描述

    😉


    程序分析:

    1 )编译阶段:

    编译器只知道a2是Animals类型,所以编译器检查语法的时候,去Animals.class字节码文件中找move()方法,找到了,绑定上move()方法,编译就通过了,静态绑定成功。(编译阶段属于静态绑定)

    2)运行阶段:

    实际上堆内存中创建的对象是Cat对象,调用move()方法的对象是new Cat()出来的,所以运行阶段执行Cat对象的move方法,此过程为运行阶段绑定。(运行阶段属于动态绑定
    在这里插入图片描述
    运行阶段和底层堆内存中的实际对象相关,执行时会自动调用实际堆内存中实际对象的相关方法。

    在这里插入图片描述

    5、多态之向下转型

    当调用的方法是父类有的时候,如:a2.move(),静态绑定和动态绑定阶段都没问题,但当父类型的引用调用的是子类型对象中特有的方法时,静态绑定的编译阶段就会出错,此时就需要向下转型。

    //向下转型的前提是有继承关系
    
    Animals a5 = new Cats();
    //用一个Cats类型的变量去装转型的Animals类型的a5
    Cats x = (Cats)a5;
    x.catchMouse();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    向下转型成功是因为,编译器看a5是Animals类型,而要转的类Cats和Animals有继承关系,所以成功。而当一个父类有多个子类的时候,即使要转的类和实际对象的类不一样,只要有继承关系,也能转成功。

    //正常new对象加一个简单的多态
    Animals a6 = new Birds();
    
    //Cats类和a6类型的Animals类有继承关系,能转型成功
    Cats y = (Cats)a6;
    
    //y是Cats类型,有catchMouse()方法,编译通过
    y.catchMouse();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    运行的时候,那Birds类的对象转成Cat类的对象,出错了。
    在这里插入图片描述

    这里是引用
    经典错误—java.lang.ClassCastException,类型转换异常

    在这里插入图片描述

    6、instanceof–避免ClassCastException

    instanceof的整理:

    • instanceof可在运行阶段动态判断引用指向的对象的类型
    • instanceof的语法:
    (引用 instanceof 类型)
    
    • 1
    • instanceof运算符的结果只能是true/false
    • 若(c instanceof Cat)结果为true,则说明引用c指向的堆内存中的对象是Cat类型,反之则说明这个引用指向的对象不是Cat类型

    由此之前的ClassCastException:

    Animal a6 = new Bird();
    Cat y = (Cat)a6;
    y.catchMouse();
    
    • 1
    • 2
    • 3

    可修改为:

    if (a6 instanceof Cat){
    	Cat y = (Cat)a6;
    	y.catchMouse();
    }
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    为避免ClassCastException,Java规范中要求向下转型必须使用instanceof.

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    思考,main方法中,我以前看到引用x指向Birds()对象,干嘛还if…else if


    大项目中合作分工,若有个方法的形参是Animals a,如public void test (Aniamls a){ },调用者传入的可能是Animals类型,也可能是Birds类型、Cats类型,这就需要if+instanceof

    在这里插入图片描述

    7、多态在开发中的作用

    作用:

    • 降低程序的耦合度
    • 提高程序的扩展力

    软件在扩展新需求的过程中,修改的越少越好,修改的越多,系统的稳定性就越差,未知风险就越多,还可能得重新测试。

    软件开发的原则之一就是OCP原则,即开闭原则,对扩展开放,对修改关闭,直白的说就是软件扩展的过程中,对之前的东西修改的越少越好。

    在这里插入图片描述

    面向抽象编程,而不是面向具体。

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    😉

    封装、继承、多态三个面向对象的特征环环相扣。有了封装这一整体概念之后,对象和对象之间产生了继承,有了继承之后,才有了方法的覆盖和多态。

    上面多态用于程序扩展的例题中,核心是用父类型的引用做形参,调用的时候传入子类型的对象。

    在这里插入图片描述

    **静态方法不谈覆盖。**因为哪怕你覆盖了,并强行用”引用.“,编译时也会当类名.来处理,进而没有多态这一说,覆盖也就变的没意义了。

    在这里插入图片描述

    public class Override {
        //定义私有方法
        private void test(){
            System.out.println("私有的");
    
        }
        public static void main(String[] args) {
            Override override2 = new Override2();
            override2.test();
    
        }
    
    }
    
    class Override2 extends Override{
        //尝试给更高的权限来覆盖
        public void test(){
            System.out.println("尝试覆盖");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    运行结果不是覆盖后的test()方法;

    在这里插入图片描述

    私有的方法不能被覆盖,静态的方法不谈覆盖!!

    在这里插入图片描述
    关于方法覆盖时返回值类型,若原先是基本数据类型,则重写前后必须一致,若是引用数据类型,重写时可以变的更小,不能变的更大。(把Animals返回值类型的变成它子类型Cat的可以,变成Object类型的不行)
    在这里插入图片描述
    重写方法时,直接粘贴一份父类的代码过来,再修改,这样最干脆。

  • 相关阅读:
    Linux之initd管理系统(海思、ZYNQ、复旦微)添加密码登录验证
    外贸人如何向国外客户展现我们的合作诚意
    【LeetCode动态规划#07】01背包问题一维写法(状态压缩)实战,其二(目标和、零一和)
    企业数据安全重要?私有化部署IM,保障信息安全无忧虑!
    2022年终巨献:一份拿下了阿里、网易、滴滴等大厂offer的学习笔记
    安装rabbitMQ
    每天一个数据分析题(三百零五)
    《Vue入门到精通之webpack详解》
    mongoDB如何根据条件分组汇总
    接口测试工具之Postman详解
  • 原文地址:https://blog.csdn.net/llg___/article/details/127544646