多态(polymorphic)

2023-11-14

目录 

1. 多态的基本介绍

2. 多态实现条件

3. 重写

重写的介绍:

【重写和重载的区别】

动、静态绑定机制

5  向上转型和向下转型

向上转型

 向上转型的特点(总结):

向下转型

多态的优缺点


多态是Java三大基本特征中最抽象也是最重要的特征。多态是建立在封装和继承衍生之上的。

1. 多态的基本介绍

 多(多种)态(状态)。                                                                                                                多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同 的状态。

举个例子:动物有很多种类,狗、猫等等,在吃这个条件下;猫吃猫粮,狗吃狗粮。这就是多态的具体体现。

总之就是:同一件事情,发生在不同对象身上,就会产生不同的结果。

2. 多态实现条件

在java中要实现多态,必须要满足如下几个条件,缺一不可:
1. 必须在继承体系下
2. 子类必须要对父类中方法进行重写
3. 通过父类的引用调用重写的方法

多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。

案例:

public class demo {
    public static void main(String[] args) {
        // 编译器在编译代码时,并不知道要调用Dog 还是 Cat 中eat的方法
        // 等程序运行起来后,形参a引用的具体对象确定后,才知道调用那个方法
        // 注意:此处的形参类型必须时父类类型才可以
        Animal dog = new Dog();
        dog.eat();
        Animal cat = new Cat();
        cat.eat();
    }
}
class Animal {
    public void eat() {
        System.out.println("吃饭");
    }
}
class Dog extends Animal {
    public String name;
    public void bark() {
        System.out.println("汪汪汪!");
    }
}
class Cat extends Animal {
    public void miaow() {
        System.out.println("喵喵喵!");
    }
}

 这个就是一个多态的向上转型,Dog dog = new Dog() ; 这没问题,而现在dog的类型为Animal了,类型不一样;理论上来说类型不一样的不可以这么赋值,为什么这里可以写,那是因为二者构成了父子类关系。

那现在我们去试着调用子类中特有的方法:

 编译似乎不然通过,这是为什么呢?

当我们的 " . " 之前的类型里,是必须包含 " . " 之后的成员或方法,否则编译不通过。当我们调用bark的时候,Animal中找不到Dog指向的bark,所以无法运行。

结论: 当我们发生向上转型的时候,我们只能调用父类有的成员和方法,不能调用子类特有的成员和方法。

向上转型是多态发生的基础。

还有其他向上转型的其他例子:

public static void func(Animal animal){
        
    }
    public static void main(String[] args) {
        Dog dog = new Dog();
        func(dog);
    }
 public static Animal func(){
        return new Dog();
    }
    public static void main(String[] args) {
        Dog dog = new Dog();
        func();
    }

3. 重写

我们上面猫和狗吃饭有点不太符合我们的实际需求,狗就吃狗粮,猫就吃猫粮,并且我不想在父类中对eat进行重写。

我们将代码进行修改:

public class demo {

    public static void main(String[] args) {
        Animal dog = new Dog();
        dog.name = "小金毛";
        dog.eat();
        Animal cat = new Cat();
        cat.name = "小折耳";
        cat.eat();
    }
}
class Animal {
    public String name;
    public int age;
    public void eat() {
        System.out.println("吃饭");
    }
}
class Dog extends Animal {
    public void bark() {
        System.out.println("汪汪汪!");
    }

    @Override
    public void eat() {
        System.out.println(name + "吃狗粮");
    }
}
class Cat extends Animal {
    public void miaow() {
        System.out.println("喵喵喵!");
    }
    @Override
    public void eat() {
        System.out.println(name + "吃猫粮");
    }
}

注意看:

 子类中有一个与父类同名的方法上面一行有一个 " @Override " 这个就是注解,注解有很多中,这里的   " @Override "   是指对重写的方法进行检查,检查你是否发生了重写,如果没有发生会报错。

重写的介绍:

重写(override):也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程
进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定
于自己的行为。 也就是说子类能够根据需要实现父类的方法。

【方法重写的规则】
1. 子类在重写父类的方法时,一般必须与父类方法原型一致: 返回值类型 方法名 (参数列表) 要完全一致
2. 被重写的方法返回值类型可以不同,但是必须是具有父子关系的
3. 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方法就不能声明为 protected
4. 父类被static、private修饰的方法、构造方法都不能被重写。
5. 重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验. 例如不小心将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就会编译报错, 提示无法构成重写.

【重写和重载的区别】

区别点 重写(override) 重载(override)
参数列表 一定不能修改 必须修改
返回类型 一定不能修改【除非可以构成父子类关系】 可以修改
访问限定符 一定不能做更严格的限制(可以降低限制) 可以修改

动、静态绑定机制

至于重写是怎么发生的,这里需要讲一个动态绑定机制。

Java的动态绑定机制(非常重要):

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

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

动态绑定发生条件:

1. 向上转型

2. 重写

3. 通过父类引用调用父类和子类重写的方法

 静态绑定:也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代表:方法的重载。

5  向上转型和向下转型

向上转型

向上转型的本质:父类的引用指向了子类的对象。

语法:父类类型 对象名 = new 子类类型()

例如:

Animal animal = new Cat("元宝",2);

animal是父类类型,但可以引用一个子类对象,因为是从小范围向大范围的转换。

猫属于动物,但是动物不仅仅只有猫。

【使用场景】
1. 直接赋值
2. 方法传参
3. 方法返回

案例不做演示,和上面的案例差不多。

 向上转型的特点(总结):

1. 可以调用父类的所有成员,但是需要遵守权限。

2. 不能调用子类特有的成员、方法。//因为在编译阶段,能调用哪些成员是由编译类型决定的

3. 最终运行效果看子类的具体表现,即调用时从子类开始查找方法,然后调用。

向上转型的优点:让代码实现更简单灵活。
向上转型的缺陷:不能调用到子类特有的方法

向下转型

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

例如: Cat cat = (Cat) animal;

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

向上转型是在堆上创建一个子类的对象把地址赋给了父类的引用,向下转型就是这个父类的引用把开辟的子类的空间的地址回到了子类的引用,所以可以使用子类独有的方法。

 将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转换。

public class TestAnimal {
    public static void main(String[] args) {
        Cat cat = new Cat("元宝",2);
        Dog dog = new Dog("小七", 1);
// 向上转型
        Animal animal = cat;
        animal.eat();
        animal = dog;
        animal.eat();
// 编译失败,编译时编译器将animal当成Animal对象处理
// 而Animal类中没有bark方法,因此编译失败
// animal.bark();
// 向上转型
// 程序可以通过编程,但运行时抛出异常---因为:animal实际指向的是狗
// 现在要强制还原为猫,无法正常还原,运行时抛出:ClassCastException
        cat = (Cat)animal;
        cat.mew();
// animal本来指向的就是狗,因此将animal还原为狗也是安全的
        dog = (Dog)animal;
        dog.bark();
    }
}

向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。Java中为了提高向下转型的安全性,引入了 instanceof ,如果该表达式为true,则可以安全转换。

public class TestAnimal {
    public static void main(String[] args) {
        Cat cat = new Cat("元宝",2);
        Dog dog = new Dog("小七", 1);
// 向上转型
        Animal animal = cat;
        animal.eat();
        animal = dog;
        animal.eat();
        if(animal instanceof Cat){
            cat = (Cat)animal;
            cat.mew();
        } if(
                animal instanceof Dog){
            dog = (Dog)animal;
            dog.bark();
        }
    }
}

instanceof 关键词官方介绍:Chapter 15. Expressions

多态的优缺点

假设有如下代码:

class Shape {
    //属性....
    public void draw() {
        System.out.println("画图形!");
    }
}
class Rect extends Shape{
    @Override
    public void draw() {
        System.out.println("♦");
    }
}
class Cycle extends Shape{
    @Override
    public void draw() {
        System.out.println("●");
    }
}
class Flower extends Shape{
    @Override
    public void draw() {
        System.out.println("❀");
    }
}

【使用多态的好处】
1. 能够降低代码的 "圈复杂度", 避免使用大量的 if - else

什么叫 "圈复杂度" ?
圈复杂度是一种描述一段代码复杂程度的方式. 一段代码如果平铺直叙, 那么就比较简单容易理解. 而如果有很多的条件分支或者循环语句, 就认为理解起来更复杂.
因此我们可以简单粗暴的计算一段代码中条件语句和循环语句出现的个数, 这个个数就称为 "圈复杂度".
如果一个方法的圈复杂度太高, 就需要考虑重构.
不同公司对于代码的圈复杂度的规范不一样. 一般不会超过 10

例如我们现在需要打印的不是一个形状了, 而是多个形状. 如果不基于多态, 实现代码如下:

 public static void drawShapes() {
        Rect rect = new Rect();
        Cycle cycle = new Cycle();
        Flower flower = new Flower();
        String[] shapes = {"cycle", "rect", "cycle", "rect", "flower"};
        for (String shape : shapes) {
            if (shape.equals("cycle")) {
                cycle.draw();
            } else if (shape.equals("rect")) {
                rect.draw();
            } else if (shape.equals("flower")) {
                flower.draw();
            }
        }
    }

如果使用使用多态, 则不必写这么多的 if - else 分支语句, 代码更简单
 

public static void drawShapes() {
// 我们创建了一个 Shape 对象的数组.
    Shape[] shapes = {new Cycle(), new Rect(), new Cycle(),
    new Rect(), new Flower()};
    for (Shape shape : shapes) {
    shape.draw();
    }
}

2. 可扩展能力更强
如果要新增一种新的形状, 使用多态的方式代码改动成本也比较低.
 

class Triangle extends Shape {
    @Override
    public void draw() {
    System.out.println("△");
    }
}

对于类的调用者来说(drawShapes方法), 只要创建一个新类的实例就可以了, 改动成本很低.
而对于不用多态的情况, 就要把 drawShapes 中的 if - else 进行一定的修改, 改动成本更高.

多态缺陷:代码的运行效率降低。
1. 属性没有多态性
当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性
2. 构造方法没有多态性

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

多态(polymorphic) 的相关文章

  • Flutter安装部署运行,bug笔记

    1 根据官方文档下载flutterSDK 附带dart AndroidStudio下载flutter插件 附带dart 2 然后在flutter路径的cmd命令框 或在AS新项目目录里的文件pubspec yaml界面 或在AS命令框执行f
  • 设计模式--FlyWeight--结构型

    程序员是沟通人和机器交流 意图 Intent 运用共享技术有效地支持大量细粒度的对象 设计模式 GoF 结构 structure 面向对象的代价面向对象很好地解决了系统抽象性的问题 同时在大多数情况下 也不会损及系统的性能 但是 在某些特殊
  • 微信小程序animation

    wxml

随机推荐

  • LINUX 查询已安装的软件信息

    问题 1 列出当前主机已安装的所有RPM软件 2 查看firefox软件包的安装清单 3 查询ifconfig命令程序是安装哪个软件包后产生的 4 查看firefox软件包的用途 方案 查询所有已安装的rpm包 可以利用命令rpm qa 查
  • CentOS 修改IP地址, DNS, 网关

    一 CentOS 修改IP地址 修改对应网卡的IP地址的配置文件 vi etc sysconfig network scripts ifcfg eth0 修改以下内容 DEVICE eth0 描述网卡对应的设备别名 例如ifcfg eth0
  • 【满分】【华为OD机试真题2023 JAVA&JS】整理扑克牌

    华为OD机试真题 2023年度机试题库全覆盖 刷题指南点这里 整理扑克牌 知识点贪心排序 时间限制 1s 空间限制 256MB 限定语言 不限 题目描述 给定一组数字 表示扑克牌的牌面数字 忽略扑克牌的花色 请按如下规则对这一组扑克牌进行整
  • 【Linux】远程连接服务

    1 什么是远程连接服务器 远程连接服务器通过文字或图形接口方式来远程登录系统 让你在远程终端前登录linux主机以取得可操作主机接口 shell 而 登录后的操作感觉就像是坐在系统前面一样 2 远程连接服务器的功能 分享主机的运算能力 服务
  • 服务器传感器不显示,服务器传感器不显示

    服务器传感器不显示 内容精选 换一换 不建议将挂载至Linux系统云服务器的云硬盘卸载后 重新挂载至Windows系统云服务器 也不建议将Windows系统云服务器上的云硬盘重新挂载至Linux系统云服务器 在这种情况下 由于文件系统不一致
  • C++ Primer阅读笔记--函数重载和内联函数

    1 函数重载 main 函数不能重载 重载函数在形参数量或形参类型上有所不同 不允许两个函数除了返回类型外其他所有的要素都相同 即函数重载不允许只有返回类型不同 如果形参是某种类型的指针或引用 则通过区分其指向的是常量对象还是非常量对象可以
  • php 日期1900年开始,php日期操作函数

    header content type text html charset utf 8 总结php操作时间的函数 1 cal days in month calendar month year 函数针对指定的年份和日历 返回一个月中的天数
  • Https如何保证了数据的安全?

    Https与Http在数据传输过程的差别 Https与Http都是OSI模型中传输层协议 而唯一不同的就是Https中在Http的应用层和TCP IP增加了一个SSL TLS层 其实也是属于应用层 主要用来对数据进行加解密 保证数据的传输的
  • 服务器系统环境初始化,Centos7系统

    服务器初始化环境 更新yum源 并添加必要系统工具 修改时区 设置系统时间 ntpdate时间同步服务 修改字符集zh CN UTF 8 关闭selinux 内核优化sysctl conf 调整文件描述符ulimit 即单个进程的最大文件打
  • 模拟电路设计的九个级别,你是模电几段?

    众 生 所 搬 周 硬 知 套 模拟电路设计的九个级别 类似下围棋的段位 快来看看自己处于什么水平 感觉九段已经是世外高人了 一段 你刚开始进入这行 对PMOS NMOS BJT什么的只不过有个大概的了解 各种器件的特性你也不太清楚 具体设
  • VSCode——修改VSCode背景图片

    1 以管理员身份运行VS Code 安装background插件 2 打开设置 在搜索框中输入background 选择扩展中的plugin background 选择在setting json中编辑 3 在用户设置中输入以下代码 修改完后
  • [QT入门篇]信号槽机制

    一 信号与槽的引入 信号与槽 Signal Slot 是 Qt 编程的基础 信号槽 实际是观察者模式 发布 订阅模式 当某个事件发生之后 比如 按钮检测到自己被点击了一下 它就会发出一个信号 signal 这种发出是没有目的的 类似广播 如
  • 【NLP】第 1 章 :机器阅读理解简介

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • Python可视化-气泡图

    气泡图类似散点图 也是表示XY轴坐标之间的变化关系 也可以像彩色散点图给点上色 区别在于可以通过图中散点的大小来直观感受其所表示的数值大小 一 数据文件准备 1 PeopleNumber csv city people price NJ 8
  • Android VNDK的作用

    本文目的是让你理解VNDK是什么作用 暂不做细节讲解 在Android 8 0及更高版本中 引入了很多技术都是为了将system和vendor分离 这样设备厂商和芯片供应商只需关心vendor实现 那么vendor实现时要加载framewo
  • 【总结】前端常用编码写法合集

    一 css样式 1 文字多行溢出 单行溢出 overflow hidden white space nowrap text overflow ellipsis 多行溢出 display webkit box webkit box orien
  • Vue教程(一):Vue核心

    Vue教程 一 Vue核心 1 1 Vue简介 1 1 1 Vue是什么 一套用于构建用户界面的渐进式JS框架 1 1 2 谁开发的 尤雨溪 2015 10 27 正式发布 Vue1 0 0 Evangelion 新世纪福音战士 2016
  • C语言 结构体初阶

    头文件 define CRT SECURE NO WARNINGS 1 include
  • 毕设系列三之利用tensorflow做深度学习情感分析

    利用tensorflow做深度学习情感分析 深度学习作为一项学习数据的多层特征或表征的强大机器学习技术 此项目中 将使用tensorflow深度学习平台 通过相关模型的构建 以及数据的处理 完成微博评论情感分析 已到达类似百度AI情感分析功
  • 多态(polymorphic)

    目录 1 多态的基本介绍 2 多态实现条件 3 重写 重写的介绍 重写和重载的区别 动 静态绑定机制 5 向上转型和向下转型 向上转型 向上转型的特点 总结 向下转型 多态的优缺点 多态是Java三大基本特征中最抽象也是最重要的特征 多态是