2-5Java多态

news/2024/12/21 20:07:00/文章来源:https://www.cnblogs.com/positive-boy/p/18405297

Java 多态

多态是同一个行为具有多个不同表现形式或形态的能力。

多态就是同一个接口,使用不同的实例而执行不同操作,如图所示:

1725885721105

多态性是对象多种表现形式的体现。

现实中,比如我们按下 F1 键这个动作:

  • 如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;
  • 如果当前在 Word 下弹出的就是 Word 帮助;
  • 在 Windows 下弹出的就是 Windows 帮助和支持。

同一个事件发生在不同的对象上会产生不同的结果。

多态的优点

  • 消除类型之间的耦合关系
  • 可替换性
  • 可扩充性
  • 接口性
  • 灵活性
  • 简化性

多态存在的三个必要条件

  • 继承
  • 重写
  • 父类引用指向子类对象

比如:

`Parent p = ``new` `Child();`

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。

多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。

以下是一个多态实例的演示,详细说明请看注释:

`public` `class` `Test {``  ``public` `static` `void` `main(String[] args) {``   ``show(``new` `Cat()); ``// 以 Cat 对象调用 show 方法``   ``show(``new` `Dog()); ``// 以 Dog 对象调用 show 方法` `   ``Animal a = ``new` `Cat(); ``// 向上转型 ``   ``a.eat();        ``// 调用的是 Cat 的 eat``   ``Cat c = (Cat)a;    ``// 向下转型 ``   ``c.work();    ``// 调用的是 Cat 的 work`` ``} ` `  ``public` `static` `void` `show(Animal a) {``   ``a.eat(); ``    ``// 类型判断``    ``if` `(a ``instanceof` `Cat) { ``// 猫做的事情``      ``Cat c = (Cat)a; ``      ``c.work(); ``    ``} ``else` `if` `(a ``instanceof` `Dog) { ``// 狗做的事情``      ``Dog c = (Dog)a; ``      ``c.work(); ``    ``} ``  ``} ``}` `abstract` `class` `Animal { ``  ``abstract` `void` `eat(); ``} ` `class` `Cat ``extends` `Animal { ``  ``public` `void` `eat() { ``    ``System.out.println(``"吃鱼"``); ``  ``} ``  ``public` `void` `work() { ``    ``System.out.println(``"抓老鼠"``); ``  ``} ``} ` `class` `Dog ``extends` `Animal { ``  ``public` `void` `eat() { ``    ``System.out.println(``"吃骨头"``); ``  ``} ``  ``public` `void` `work() { ``    ``System.out.println(``"看家"``); ``  ``} ``}`

执行以上程序,输出结果为:

`吃鱼``抓老鼠``吃骨头``看家``吃鱼``抓老鼠`

虚函数

虚函数的存在是为了多态。

Java 中其实没有虚函数的概念,它的普通函数就相当于 C++ 的虚函数,动态绑定是Java的默认行为。如果 Java 中不希望某个函数具有虚函数特性,可以加上 final 关键字变成非虚函数。

重写

我们将介绍在 Java 中,当设计类时,被重写的方法的行为怎样影响多态性。

我们已经讨论了方法的重写,也就是子类能够重写父类的方法。

当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。

要想调用父类中被重写的方法,则必须使用关键字 super

`public` `class` `Employee {``  ``private` `String name;``  ``private` `String address;``  ``private` `int` `number;``  ``public` `Employee(String name, String address, ``int` `number) {``   ``System.out.println(``"Employee 构造函数"``);``   ``this``.name = name;``   ``this``.address = address;``   ``this``.number = number;``  ``}``  ``public` `void` `mailCheck() {``   ``System.out.println(``"邮寄支票给: "` `+ ``this``.name``    ``+ ``" "` `+ ``this``.address);``  ``}``  ``public` `String toString() {``   ``return` `name + ``" "` `+ address + ``" "` `+ number;``  ``}``  ``public` `String getName() {``   ``return` `name;``  ``}``  ``public` `String getAddress() {``   ``return` `address;``  ``}``  ``public` `void` `setAddress(String newAddress) {``   ``address = newAddress;``  ``}``  ``public` `int` `getNumber() {``   ``return` `number;``  ``}``}`

假设下面的类继承Employee类:

`public` `class` `Salary ``extends` `Employee {``  ``private` `double` `salary; ``// 全年工资``  ``public` `Salary(String name, String address, ``int` `number, ``double` `salary) {``    ``super``(name, address, number);``    ``setSalary(salary);``  ``}``  ``public` `void` `mailCheck() {``    ``System.out.println(``"Salary 类的 mailCheck 方法 "``);``    ``System.out.println(``"邮寄支票给:"` `+ getName()``    ``+ ``" ,工资为:"` `+ salary);``  ``}``  ``public` `double` `getSalary() {``    ``return` `salary;``  ``}``  ``public` `void` `setSalary(``double` `newSalary) {``    ``if``(newSalary >= ``0.0``) {``     ``salary = newSalary;``    ``}``  ``}``  ``public` `double` `computePay() {``   ``System.out.println(``"计算工资,付给:"` `+ getName());``   ``return` `salary/``52``;``  ``}``}`

现在我们仔细阅读下面的代码,尝试给出它的输出结果:

`/* 文件名 : VirtualDemo.java */``public` `class` `VirtualDemo {``  ``public` `static` `void` `main(String [] args) {``   ``Salary s = ``new` `Salary(``"员工 A"``, ``"北京"``, ``3``, ``3600.00``);``   ``Employee e = ``new` `Salary(``"员工 B"``, ``"上海"``, ``2``, ``2400.00``);``   ``System.out.println(``"使用 Salary 的引用调用 mailCheck -- "``);``   ``s.mailCheck();``   ``System.out.println(``"\n使用 Employee 的引用调用 mailCheck--"``);``   ``e.mailCheck();``  ``}``}`

以上实例编译运行结果如下:

`Employee 构造函数``Employee 构造函数``使用 Salary 的引用调用 mailCheck --``Salary 类的 mailCheck 方法``邮寄支票给:员工 A ,工资为:``3600.0` `使用 Employee 的引用调用 mailCheck--``Salary 类的 mailCheck 方法``邮寄支票给:员工 B ,工资为:``2400.0`

例子解析

  • 实例中,实例化了两个 Salary 对象:一个使用 Salary 引用 s,另一个使用 Employee 引用 e。
  • 当调用 s.mailCheck() 时,编译器在编译时会在 Salary 类中找到 mailCheck(),执行过程 JVM 就调用 Salary 类的 mailCheck()。
  • 因为 e 是 Employee 的引用,所以调用 e 的 mailCheck() 方法时,编译器会去 Employee 类查找 mailCheck() 方法 。
  • 在编译的时候,编译器使用 Employee 类中的 mailCheck() 方法验证该语句, 但是在运行的时候,Java虚拟机(JVM)调用的是 Salary 类中的 mailCheck() 方法。

以上整个过程被称为虚拟方法调用,该方法被称为虚拟方法。

Java中所有的方法都能以这种方式表现,因此,重写的方法能在运行时调用,不管编译的时候源代码中引用变量是什么数据类型。

多态的实现方式

方式一:重写:

这个内容已经在上一章节详细讲过,就不再阐述,详细可访问 Java 重写与重载 这一章节的内容。

方式二:接口

  • 生活中的接口最具代表性的就是插座,例如一个三接头的插头都能接在三孔插座中,因为这个是每个国家都有各自规定的接口规则,有可能到国外就不行,那是因为国外自己定义的接口类型。
  • java中的接口类似于生活中的接口,就是一些方法特征的集合,但没有方法的实现。具体可以看java接口这一章节的内容。

方式三:抽象类和抽象方法

详情请看 Java抽象类 这一章节的内容。

自己的理解:

多态是同一行为具有多个不同表现形式或形态的能力,书上说依靠重写,继承,父类指向子类对象,那为什么呢?我的理解是多态是用父类类型的引用来创建子类的实例,所以这里肯定用到了继承,然后得到的这个实例对象调用的是子类里面重写的方法,而不是父类里面被重写的方法,如果想要调用父类的被重写的方法需要用到super在子类方法,但是想要调用到子类里面重写的方法的前提是父类中也有这个函数,如果没有则会发生编码错误

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/794657.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【VMware by Broadcom】Fusion 产品下载汇总

Fusion 产品下载汇总(百度网盘)-『2024年9月9日更新』Fusion 产品版本 百度网盘VMware-Fusion-1.0.0-51348.dmg 链接:https://pan.baidu.com/s/1C8Qkr6nwV5rKrhpsv2JJ_A?pwd=t0kjVMware-Fusion-1.1.0-62573.dmgVMware-Fusion-1.1.1-72241.dmgVMware-Fusion-1.1.2-87978.dmg…

C#/.NET/.NET Core技术前沿周刊 | 第 4 期(2024年9.1-9.8)

前言 C#/.NET/.NET Core技术前沿周刊,你的每周技术指南针!记录、追踪C#/.NET/.NET Core领域、生态的每周最新、最实用、最有价值的技术文章、社区动态、优质项目和学习资源等。让你时刻站在技术前沿,助力技术成长与视野拓宽。欢迎投稿,推荐或自荐优质文章/项目/学习资源等。…

51nod 1051 最大子矩阵和

51nod 1051 最大子矩阵和 可以用前缀和容斥优化到 \(O(n^4)\),但是不够进行如下图操作:将每一列的数值都压缩到一维的数组上,就转换为求最大字段和问题,时间复杂度 \(O(n^3)\)。 看看代码就知道了。 #include <bits/stdc++.h> using namespace std; #define ll long …

VS中如何将本地代码上传到码云仓库

VS中如何将本地代码上传到码云仓库 方式一:点击“添加到源代码管理”VS底部栏点击“添加到源代码管理”,并选择“Git”选项在弹出窗口中,选择“其他→现有远程”选项,在右侧区域找到“远程URL”输入框,输入Gitee仓库地址,然后点击“创建并推送”按钮。此时项目目录会多出…

Linux下网络丢包故障定位

转载: 云网络丢包故障定位全景指南 硬件网卡丢包 Ring Buffer溢出如图所示,物理介质上的数据帧到达后首先由NIC(网络适配器)读取,写入设备内部缓冲区 Ring Buffer中,再由中断处理程序触发 Softirq 从中消费,Ring Buffer 的大小因网卡设备而异。当网络数据包到达(生产)…

第一次个人编程作业

github地址这个作业属于哪个课程 计科22级12班这个作业要求在哪里 作业要求链接这个作业的目标 遍历论文查重并封装成可执行文件,学习PSP和commit规范,学习测试和评估代码一、设计思路 文件结构:程序流程:实现逻辑:查找资料发现比较简单的实现是通过计算余弦向量来实现重复…

echart map图标切换多选,单选,默认选中

需求是echart默认地图选中之前的去过的城市,一开始多选,后面点击为单选const option = {tooltip: {trigger: item,formatter: {b}},series: [{type: map,roam : true,//是否开启缩放和平移zoom : 1,//当前视角缩放比例selectedMode: multiple, // 只允许单选// 设置为一张完整…

CH58x/CH59x/CH57x RF_PHY(2.4g)切换Channel发送接收

前言:在做某些应用的时候可能需要我们发送或者接收时切换对应的channel。 此次完成测试的平台在WCH的CH592F上完成的。 在工作发送过程中切换37、38、39三个信道进行轮询发送。具体需要使用最关键的函数是:RF_SetChannel 实现代码如下:if(events & channl_37_tx_evt){RF…

ArmSoM-Sige5 的 RK3576 SoC 主线内核支持进展

我们很高兴地宣布,基于 RK3576 SoC 的 ArmSoM-Sige5 开发板的主线内核支持,collabora正在稳步推进中。RK3576 SoC 是 Rockchip 家族的一员,其设计和功能与广受欢迎的 RK3588 相似,许多硬件模块都得到了复用,这为我们在主线内核中添加支持提供了有利条件。 RK3576主线内核…

P3579

今天有点高效啊,切数论题都这样喵? #include<bits/stdc++.h> using namespace std; int main() {int n,a,b,c,d,s,m;cin>>n;while(n--){cin>>a>>b>>c>>d; m=min(b,d);for(int i=1;i<=m;i++){i=min(b/(b/i),d/(d/i));//优化,只考虑b/…

机器学习作业

Ch3-K均值聚类算法 【9月4日】 学号:102102156 姓名:高涛 1. make_circles方法生成数据 1.1 源代码 from sklearn.cluster import KMeans from sklearn.datasets import make_circles, make_moons, make_blobs import matplotlib.pyplo…

volta 管理多个node版本时,Volta error: Could not download node

设置代理 在终端中执行以下命令,替换为你自己的代理地址: bash $env:HTTP_PROXY="你的代理地址" $env:HTTPS_PROXY="你的代理地址" 然后重启终端: Windows 用户需要以管理员身份重新打开终端。 Mac 用户只需重启终端即可。 这样可以确保你在终端中通过代…