本篇博客主要介绍Java中多态的实现方法。

多态


在学习多态之前,我们先来看几个和多态相关的概念
我们写几个类来用来演示

public class People {public String name;public People(String name) {this.name = name;}public void buyTicket() {System.out.println(this.name + "正在买票!");}
}
public class Adult extends People{public Adult(String name) {super(name);}
}
public class Student extends People{public Student(String name) {super(name);}
}

向上转型


一句话来说明,向上转型就是使用一个父类的引用,指向一个子类的对象。如下:

People p1 = new Student("小明");
People p2 = new Adult("潘爱国");

为什么叫向上转型呢

  • 在面向对象的程序设计中,针对一些复杂的场景(很多类,很复杂的继承关系),程序猿会画一种叫做UML图的方式来表示类之间的关系。此时父类通常画在子类的上方。所以我们就称为“向上转型”,表示往父类的方向转。

向上转型发生的时机

  • 直接赋值
  • 方法传参
  • 方法返回

有向上转型,自然就有向下转型

  • 向下转型就是将一个用父类引用指向的对象改为由子类引用来指向
  • 但是这个转换是有前提的,前提是父类引用指向的对象本身就是子类对象。否则就会转换出错


    所以我们需要在向下转型之前判断一下一个引用引用的是否是某个类型的对象,然后再进行向下转型使用instanceof可以实现这个功能
  • 向下转型的使用场景People people = new Student();这里使用一个父类引用指向一个子类对象,子类Student中有一个方法上学goToSchool();这时候我们使用父类引用来调用这个子类方法people.goToSchool();



    可以看到这里找不到goToSchool();方法,这是为什么呢?我们的people引用明明指向的是一个Student类对象。
    这是因为,编译过程中people的类型是People,此时编译器只知道这个类中有一个buyTicket();方法,没有goToSchool();方法。虽然people实际引用的是一个Student类对象,但是编译器是以People的类型来查看有哪些方法的
    对于People people = new Student();这样的代码。编译器检查有哪些方法存在时,看的是People这个类型执行时究竟执行父类的方法还是子类的方法,看的是Student类型
    所以这个时候如果想要正常执行,达到我们想要的效果,就需要使用向下转型

动态绑定


我们给Adult和Student子类都添加一个和父类同名的方法,来看一下


下面,我们来看一段代码演示

  • 代码如下
  • 运行结果如下

从运行结果,我们可以发现

  • p1和p2虽然都是People类型的引用,但是p1指向People类型的实例p2指向Student类型的实例
  • p1和p2分别调用buyTicket方法时,发现p1.buyTicket()调用的是People类的方法p2.buyTicket()调用的是Student类的方法

结论

  • 在Java中,调用某个类的方法,究竟执行了哪段代码(是父类方法的代码还是子类方法的代码),要看究竟这个引用指向的是父类对象还是子类对象这个过程是程序运行时决定的(而不是编译期),因此称为动态绑定

方法重写


对于上述,子类和父类中存在同名方法,并且参数的类型和个数完全相同,返回类型也必须相同,这种情况称为覆写/重写/覆盖(override)

关于重写的注意事项

  • 重写和重载完全不一样。不要混淆。
    重载要求方法名称相同,参数的类型及个数不同,返回值类型没有影响重载的函数在一个类中,没有权限要求
    重写要求方法名称、返回值类型、参数的类型及个数必须完全相同;必须是继承关系中的两个方法;被覆写的方法不能拥有比父类更严格的访问控制权限
  • 普通方法可以重写,static修饰的静态方法不能重写

  • 重写中子类方法的访问权限不能低于父类方法的访问权限日常使用中,我们经常将二者的访问权限设置成一样

  • 如果一个方法不能被继承,则不能重写它,例如:父类中private权限的方法


  • 针对重写方法,可以使用@Override注解来显式指定
    使用注解有两方面的功能:①能够让代码读者更好的理解这个方法是重写的;②能够在编译器做出一些检查,如果没有构成重写,能够检查出来

理解多态


理解了向上转型、动态绑定和方法重写之后,我们就可以使用多态(polypepride)的形式来设计程序
我们来写一个买票的例子,来理解多态:

  • 代码如下
    首先,来写一个类表示人,其中有一个买票方法,什么也不做

    然后,继承该人类,写两个子类成人和学生,二者中分别对买票方法进行重写


    最后,我们写一个买票的函数,将两个对象传进去,感受一下多态的作用。前面为类的实现者,下面为类的使用者。


    这里,我们可以看到,当类的调用者在编写buyTickets()这个方法的候,参数类型为People(父类),此时在该方法内部并不知道,也不关心当前的people引用指向的是哪个类型的实例此时people这个引用调用buyTicket()方法可能会有多种不同的表现(和people对应的实例有关),这种行为就称为多态
    多态顾名思义,就是“一个引用,能表现出多种不同形态”。

使用多态的好处


  • 类调用者对类的使用成本进一步降低
    封装是让类的调用者不需要知道类的实现细节
    多态能让类的调用者连这个类的类型是什么都不必知道,只需要直到这个对象具有某个方法即可
    因此,多态也可以理解成是封装的更进一步,让类调用者对类的使用成本进一步降低。
  • 能够降低代码的“圈复杂度”,避免使用大量if-else
    什么是圈复杂度
    圈复杂度是一种描述一段代码复杂程度的方式。一段代码如果平铺直叙,那么就比较简单容易理解。而如果有很多的条件分支或者循环语句,就认为理解起来更复杂
    因此我们可以简单粗暴的计算一段代码中条件语句和循环语句出现的个数这个个数就称为“圈复杂度”。如果一个方法的圈复杂度太高,就需要重构。
    不同公司对于代码的复杂度的标准不一样,一般不会超过10

假如现在我们有5个人买票,不使用多态,代码如下


使用多态,代码如下


可以看到使用多态,少了if-else分支,代码的圈复杂度明显降低

  • 可扩展能力强
    还是前面的例子,如果我们需要在业务逻辑中加上另外一类人儿童,买票免费,我们来看一下使用多态和不使用多态分别需要怎么修改
    首先,我们先将儿童类创建出来

    看一下不使用多态,代码修改情况


    再来看一下使用多态的代码修改情况

总结

多态其实是一个更广泛的概念,和“继承”这样的语法并没有必然的联系

  • C++中的“动态多态”和Java的多态类似。但是C++中还有一种静态多态(函数重载、泛型编程),就和继承没有关系了
  • Python中的多态体现的是“鸭子类型”,也和继承体系没有任何关系
  • Go语言中没有“继承”这样的概念,同样也能表示多态

无论是哪种编程语言,多态的核心都是让调用者不必关注对象的具体类型。这是降低用户使用成本的一种重要方式

『JavaSE』多态相关推荐

  1. 『JavaSE』泛型和包装类

    本篇博客主要介绍Java泛型相关的知识和包装类的相关知识的简单认识. 泛型 Java中的泛型和C++的泛型大概是相似的: Java泛型参数只可以代表类,不能代表个别对象: Java编译器在编译泛型时会 ...

  2. 『转载』Debussy快速上手(Verdi相似)

    『转载』Debussy快速上手(Verdi相似) Debussy 是NOVAS Software, Inc(思源科技)发展的HDL Debug & Analysis tool,这套软体主要不是 ...

  3. 『参考』.net CF组件编程(4)——为自定义组件添加工具箱图标!

    前言: 在前三篇的文章中,和大家一起创建了一个用于TCP连接检测的小组件,如果你记不得了,可以通过以下链接去回顾一下: 『参考』.net CF组件编程(1)--基础之后 『参考』.net CF组件编程 ...

  4. 『TensorFlow』命令行参数解析

    argparse很强大,但是我们未必需要使用这么繁杂的东西,TensorFlow自己封装了一个简化版本的解析方式,实际上是对argparse的封装 脚本化调用tensorflow的标准范式: impo ...

  5. 『Numpy』常用方法记录

    numpy教程 防止输出省略号 import numpy as np np.set_printoptions(threshold=np.inf) 广播机制 numpy计算函数返回默认是一维行向量: i ...

  6. 2018年『web』开发者不得不知的技术趋势

    作为一个『web』开发者,无论是做前端还是后端,都应该时刻保持着对技术的敏感性.技术的流行需要一定时间的沉淀,有哪些web相关的技术会可能会在2018年成为web开发的新宠呢?下面列举业界经过实践并且 ...

  7. 『TensorFlow』函数查询列表_张量属性调整

    博客园 首页 新随笔 新文章 联系 订阅 管理 『TensorFlow』函数查询列表_张量属性调整 数据类型转换Casting 操作 描述 tf.string_to_number (string_te ...

  8. 『TensorFlow』专题汇总

    TensorFlow函数查询 『TensorFlow』0.x_&_1.x版本框架改动汇总 『TensorFlow』函数查询列表_数值计算 『TensorFlow』函数查询列表_张量属性调整 『 ...

  9. 两个构件的重合点_初三物理易错点:你被眼睛欺骗了,那些『平面镜成像』中的困惑...

    时光飞逝,一晃初三的伙伴们已经返校复课一个多月了,想必有很多感慨和困惑吧? 还有的地方初三的伙伴们还没有复课,在家乖乖地学习网课,想必也是有诸多的困惑和不解吧? 今天木木老西跟大家解开物理当中的一个困 ...

最新文章

  1. 硬件Pythia:将现实世界桥接到区块链
  2. 前端服务器OWA 访问显示异常最佳解决方案
  3. 统计字符串中单词个数的算法优化
  4. reids mysql 面试_月薪3k的后端面试点-Mysql和Redis
  5. 浅析GDAL库C#版本支持中文路径问题(续)
  6. Windows集群网络负载均衡
  7. [译] REST API 已死,GraphQL 长存
  8. c++ 写并查集算法模板
  9. 安全漏洞之host头攻击漏洞
  10. python翻转课堂_翻转课堂
  11. php网页显示左中,php的动态页面在ie内核的浏览器面整体偏左的解决方法静
  12. Loadrunner11.00破解方法
  13. 用java基础实现五子棋
  14. android uinput 按键_Android 触摸屏Event上报操作
  15. java把一段英文拆成单词_如何在java中将句子拆分成单词和标点符号
  16. 20230411-赛码网刷题
  17. 视频播放器上实现AirPlay投屏功能
  18. 原来光刻机核心技术,来自一个华人
  19. 用vue实现城市选择组件
  20. 生产者消费者操作系统实验报告用C语言来实现

热门文章

  1. php 去除emoji
  2. JAVA通过Graphics2D生成表格图片
  3. 泛微:打造协同办公OA第一品牌
  4. 有什么图片翻译软件?快把这些软件收好
  5. 打印机smb扫描显示服务器,您好,打印机扫描完成后显示:通过SMB发送失败,错误码2101.是怎么回事啊,求指点。...
  6. 微信小程序使用setData方法修改data中对象或数组的属性值
  7. 【OnMyWay】我为啥弃用了Ubuntu
  8. uniapp实现下拉搜索选择框,app,h5可用
  9. ZedBoard+Vivado(一)——纯PL实现流水灯
  10. 两电源之间接0.1UF的电容起什么作用?