Java多态详解
下面讲解一下Java中的多态机制,力求用最通俗易懂的语言,最精炼的话语,最生动的例子,深入浅出Java多态,帮助读者轻松掌握这个知识点。
什么是多态?
多态是指同一种行为具有多个不同表现形式的能力。
多态的分类
多态一般分为重载式多态和重写式多态:
- 重载式多态,也叫编译时多态。也就是说这种多态在编译时已经确定好了。方法名相同而参数列表不同的一组方法就是重载。在调用这种重载的方法时,通过传入不同的参数最后得到不同的结果。
- 重写式多态,也叫运行时多态。这种多态通过动态绑定(dynamic binding)技术来实现,是指在运行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。也就是说,只有程序运行起来,你才知道调用的是哪个子类的方法。这种多态通过方法重写以及向上转型来实现。
多态实现的必要条件
- 有继承/实现关系:在多态中必须存在有继承关系的子类和父类或者接口及其实现类。
- 有方法重写:子类对父类中某些方法进行重新定义,再调用这些方法时就会调用子类的方法。
- 有父类引用指向子类对象:父类引用指向子类对象叫做向上转型。
向上转型和向下转型
- 向上转型:父类引用指向子类对象,这个是自动的,不需要显示转换。通过向上转型,你可以调用在父类中定义的方法,但不能调用子类特有的方法。
- 向下转型:子类引用指向父类对象,这个是非自动的,需要进行强制类型转换。在进行向下转型之前,通常需要使用
instanceof操作符来检查引用的对象是否确实是目标子类的实例。
多态的成员访问特点
- 成员变量:编译看左边(父类),执行看左边(父类)
- 成员方法:编译看左边(父类),执行看右边(子类)
举个栗子:
// 水果类,拥有一个show()方法
public class Fruits {public void show() {System.out.println("我水果之父,打钱!");}
}
// 苹果类,实现父类水果,并重写show()方法
public class Apple extends Fruits{@Overridepublic void show() {System.out.println("我苹果,打钱!");}public void color() {System.out.println("我是红色的苹果。");}
}
// 香蕉类,实现父类水果,并重写show()方法
class Banana extends Fruits {@Overridepublic void show() {System.out.println("我香蕉,打钱!");}public void color() {System.out.println("我是黄色的香蕉。");}
}
// 测试类
public class Test {public static void main(String[] args) {Fruits fruit = new Apple(); // 向上转型fruit.show(); // 我苹果,打钱!fruit = new Banana(); // 向上转型fruit.show(); // 我香蕉,打钱!}
}
这就是向上转型,Fruits fruit = new Apple();将子类对象Apple转化为父类对象Fruits,这个时候fruit引用指向的是子类对象,所以调用的方法是子类方法。
需要注意的是:向上转型时,子类单独定义的方法会丢失。比如,上面案例中的Apple类和Banana类都定义了自己的color方法,当进行了向上转型后,fruit引用指向Apple类的实例时是访问不到color方法的,fruit.color()会报错。
下面给出原因:
我们需要时刻记住多态的成员访问特点:编译时看左边,运行时看右边。
比如Fruits fruit = new Apple();,这行代码中的变量fruit在编译时和运行时的类型如下:
- 编译时 (Compile-time):编译时看左边,变量fruit的类型就是Fruits,这是因为我们声明了fruit为Fruits类型。在编译时,编译器只知道fruit是一个Fruits类型的引用,因此它只允许调用在 Fruits类中定义的方法,而不允许调用子类Apple中特有的方法,除非进行向下转型。
- 运行时 (Run-time):运行时看右边,fruit实际指向的对象是Apple类型的实例。这是因为我们使用new Apple()创建了一个Apple类型的对象并将其引用赋值给了fruit。因此,当我们调用 fruit.show()时,实际执行的是Apple类中重写的show()方法。
总结:在编译时,fruit的类型是Fruits,这决定了我们可以对fruit调用哪些方法。在运行时,fruit实际指向的对象是Apple类型,这决定了当我们调用fruit的方法时,实际执行的是哪个版本的方法(即Fruits类中的原始方法还是Apple类中的重写方法)。
上面讲的是向上转型,下面我们来讲一下向下转型:
// 测试类
public class Test {public static void main(String[] args) {Fruits fruit = new Apple(); // 向上转型fruit.show(); // 我苹果,打钱!fruit = new Banana(); // 向上转型fruit.show(); // 我香蕉,打钱!if (fruit instanceof Banana) {Banana banana = (Banana) fruit; // 向下转型banana.color(); // 我是黄色的香蕉。}}
}
注意:在进行向下转型之前,使用instanceof操作符进行检查是很重要的,否则,如果对象不是正确的类型,转型会抛出异常。
当使用instanceof关键字进行类型检查时,它会查看对象的运行时类型,而不是编译时类型。比如上面的例子中,fruit instanceof Banana这里fruit的引用在运行时指向一个Banana类型的对象,所以这个表达式的结果是true。简而言之,instanceof关键字总是基于对象的实际运行时类型来进行判断。
在向下转型中,子类引用指向父类对象(父类型,实例是子类的实例化),通常需要进行强制类型转换,但是这里有个需要注意的问题。
// 测试类
public class Test {public static void main(String[] args) {Fruits fruit = new Apple(); // 向上转型Apple apple = (Apple) fruit; // 向下转型,强制类型转换apple.color(); // 我是红色的苹果。Banana banana = (Banana) fruit; // 报错:java.lang.ClassCastExceptionFruits f1 = new Fruits();Apple a1 = (Apple) f1; // 报错:java.lang.ClassCastException}
}
为什么Apple apple = (Apple)fruit;没有报错可以转换成功呢?因为apple本身就是Apple对象,所以理所当然可以向下转型为Apple,因此自然也就不能转换成Banana,人可以干出指鹿为马的事情,但是编译器不行,不会指着苹果说是香蕉。
而f1是Fruits对象,它也不能被向下转型为任何子类对象,就好比你买了一个不知名的水果,你只知道它是一种水果,但是你不能直接说这个水果是苹果或者香蕉。
总结一下向下转型需要注意的问题:
- 向下转型的前提是父类引用指向的是子类对象,也就是说,向下转型之前,它得先进行过向上转型。
- 向下转型只能转型为本类对象(苹果是不能变成香蕉的)。
最后来看一个多态的经典案例:
public class A { // A类public String show(D obj) {return ("A and D");}public String show(A obj) {return ("A and A");}
}
public class B extends A { // B类public String show(B obj){return ("B and B");}public String show(A obj) {return ("B and A");}
}
public class C extends B { // C类
}
public class D extends B { // D类
}
// 测试类
public class Test {public static void main(String[] args) {A a1 = new A();A a2 = new B();B b = new B();C c = new C();D d = new D();System.out.println("1--" + a1.show(b)); // 1--A and ASystem.out.println("2--" + a1.show(c)); // 2--A and ASystem.out.println("3--" + a1.show(d)); // 3--A and DSystem.out.println("4--" + a2.show(b)); // 4--B and ASystem.out.println("5--" + a2.show(c)); // 5--B and ASystem.out.println("6--" + a2.show(d)); // 6--A and DSystem.out.println("7--" + b.show(b)); // 7--B and BSystem.out.println("8--" + b.show(c)); // 8--B and BSystem.out.println("9--" + b.show(d)); // 9--A and D}
}
前三条输出语句还好理解,从第四条开始,为什么不是输出4–B and B而是4–B and A呢?
网上博客给的一句话:当父类对象引用变量引用子类对象时,被引用对象的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在父类中定义过的,也就是说被子类覆盖的方法。这句话对多态进行了一个概括,其实在继承中对象方法的调用存在一个优先级:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。
这句话有点长,很抽象,我用最通俗易懂的来理解:
在这里,a2 是一个类型为 A 的引用,但它实际上引用的是一个 B 类型的对象。因此,当我们调用 a2.show(b) 时,以下是发生的事情:
- 编译器首先查看引用
a2的编译时类型,即A。它会检查类A中是否有一个接受B类型参数的show方法。但是,类A中并没有明确接受B类型参数的show方法。因此,编译器会选择一个更为通用的版本,即show(A obj),因为B是A的子类。 - 在运行时,JVM会查看
a2实际引用的对象类型,即B。由于B类重写了show(A obj)方法,因此JVM会调用B类中的这个版本,即B and A。
换句话说,A a2 = new B();这行代码进行了向上转型,前面说过向上转型之后,子类单独定义的方法会丢失(即B类中的show(B obj)不能被调用),那么这个时候a2可以调用的方法就剩下A类中的show(D obj)、show(A obj)以及B类中的show(A obj)。然后再根据我们的口诀“编译时看左边,运行时看右边”,当运行时a2引用的就是B对象,故最终a2.show(b)就是在调用B类中的show(A obj)。
接下来再分析第五条a2.show(c):
首先A a2 = new B();进行向上转型,那么,a2能调用的方法还是A类中的show(D obj)、show(A obj)以及B类中的show(A obj),按照继承链中调用方法的优先级,a2是A类型的引用变量,所以继承链方法调用优先级中this.show(O)的this就代表了A,显然A类中的方法不满足这个要求,跳过,所以接下来是super.show(O),A类没有父类(除了Object类),再次跳过,然后是this.show((super)O),C继承于B,B继承于A,所以show(A obj)满足要求,由于a2变量引用的对象类型是B类型,而B类型又重写了该方法,所以最终调用的是B类中的show(A obj),所以最后输出为B and A。
剩下的输出结果依次分析即可。
至此我们已经完整讲完Java多态机制,每天一个小知识点,每天进步一点点。
相关文章:
Java多态详解
下面讲解一下Java中的多态机制,力求用最通俗易懂的语言,最精炼的话语,最生动的例子,深入浅出Java多态,帮助读者轻松掌握这个知识点。 什么是多态? 多态是指同一种行为具有多个不同表现形式的能力。 多态…...
Android中简单实现Spinner的数据绑定
Android中简单实现Spinner的数据绑定 然后声明对象实例并加入到arraylist里面,并设置spinner的适配器 Spinner Sp (Spinner).............// List<CItem > lst new ArrayList<CItem>(); CItem ct new CItem ("1","测试"); lst.Add(ct)…...
【版本控制工具二】Git 和 Gitee 建立联系
文章目录 前言一、Git 和 Gitee 建立联系1.1 任意目录下,打开 git bash 命令行,输入以下命令生成公钥1.2 配置SSH公钥1.3 进行全局配置 二、其它相关Git指令2.1 常用指令2.2 指令操作可能出现的问题 三、补充3.1 **为什么要先commit,然后pull…...
最新AI智能创作系统ChatGPT商业源码+详细图文搭建部署教程+AI绘画系统
一、AI系统介绍 SparkAi创作系统是基于国外很火的ChatGPT进行开发的Ai智能问答系统。本期针对源码系统整体测试下来非常完美,可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT?小编这里写一个详细图文教程吧&am…...
【算法与数据结构】--目录
第一部分:算法基础 第一章:算法入门第二章:数据结构概述第三章:算法设计与分析 3.1 贪心算法3.2 动态规划3.3 分治算法3.4 回溯算法 第二部分:常见数据结构 第四章:数组和链表 4.1 数组4.2 链表4.3 比较…...
爱普生LQ1900KIIH复位方法
爱普生EPSON 1900KIIH是一部通用针式打印机,136列(10cpi下)的打印宽度,缓冲区128KB,打印速度为270字/秒。 打印机类型 打印方式:24针击打式点阵打印、打印方向:双向逻辑查找、安全规格标准&am…...
字段位置顺序对值的影响
Unity中验证AB加载场景时报错: Cannot load scene: Invalid scene name (empty string) and invalid build index -1 报错原因是因为把字段放在了Start函数后面(图一)改成(图二)就好了。图一中协程使用的sceneBName字段值为null。 图一: 图二:…...
pytorch_神经网络构建2(数学原理)
文章目录 深层神经网络多分类深层网络反向传播算法优化算法动量算法Adam 算法 深层神经网络 分类基础理论: 交叉熵是信息论中用来衡量两个分布相似性的一种量化方式 之前讲述二分类的loss函数时我们使用公式-(y*log(y_)(1-y)*log(1-y_)进行误差计算 y表示真实值,y_表示预测值 …...
Oracle SQL Developer 中查看表的数据和字段属性、录入数据
在Oracle SQL Developer中,选中一个表时,右侧会列出表的情况;第一个tab是字段的名称、数据类型等属性; 切换到第二个tab,显示表的数据; 这和sql server management studio不一样的; 看一下部门…...
java docker图片叠加水印中文乱码
java docker图片叠加水印中文乱码 技术交流博客 http://idea.coderyj.com/ 1.由于项目需要后端需要叠加图片水印,但是中文乱码,导致叠加了之后 中文是框框 2.经过多方查找基本都说在 linux下安装字体就解决了,但是尝试了均无效 3.后来忽然想到我的项目是用docker打包部署的,不…...
string类的使用方式的介绍
目录 前言 1.什么是STL 2. STL的版本 3. STL的六大组件 4.STL的缺陷 5.string 5.1 为什么学习string类? 5.1.1 C语言中的字符串 5.2 标准库中的string类 5.3 string类的常用接口的使用 5.3.1 构造函数 5.3.2 string类对象的容量操作 5.3.3 string类对象…...
FFmpeg 命令:从入门到精通 | 命令行环境搭建
FFmpeg 命令:从入门到精通 | 命令行环境搭建 FFmpeg 命令:从入门到精通 | 命令行环境搭建安装 FFmpeg验证 FFmpeg 是否安装成功 FFmpeg 命令:从入门到精通 | 命令行环境搭建 安装 FFmpeg 进入 FFmpeg 官网: 点击 Download&#…...
《从零开始学ARM》勘误
1. 50页 2 51页 3 236页 14.2.3 mkU-Boot 修改为: mkuboot 4 56页 修改为: 位[31:24]为条件标志位域,用f表示; 位[23:16]为状态位域,用s表示; 位[15:8]为扩展位域&…...
10款录屏软分析与选择使用,只看这篇文章就轻松搞定所有,高清4K无水印录屏,博主UP主轻松选择
录屏软件整理 如下为录屏软件,通过思维导图展示分析介绍: https://www.drawon.cn/template/details/6522bd5e0dad9029a0b528e1 如下为整理的录屏软件列表 名称产地价格支持的平台下载地址说明OBS国外免费开源windows/linux/machttps://obsproject.co…...
android: android:onClick=“@{() -> listener.onItemClick(viewModel)}“
一、前言:在我使用editTest控件的时候,它的下方有一条横线。我想把它去掉然后我在布局文件中这样写 android:background"null" 导致报错,报错信息是: android:onClick"{() -> listener.onItemClick(viewModel)…...
温故知新:dfs模板-843. n-皇后问题
n−n−皇后问题是指将 nn 个皇后放在 nnnn 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。 现在给定整数 nn,请你输出所有的满足条件的棋子摆法。 输入格式 共一行,包含整数 n…...
刷题笔记28——一直分不清的Kruskal、Prim、Dijkstra算法
图算法刷到这块,感觉像是走了一段黑路快回到家一样,看到这三个一直分不太清总是记混的名字,我满脑子想起的是大学数据结构课我坐在第一排,看着我班导一脸无奈,心想该怎么把这个知识点灌进木头脑袋里边呢。有很多算法我…...
Mysql时间同步设置
Mysql时间同步设置 当涉及到设置MySQL数据库时间与电脑同步时,实际的步骤可能会因操作系统和数据库版本的不同而有所差异。以下是一个基本的步骤示例,供您参考: 检查电脑时间: 首先确保电脑操作系统的时间是正确的。 设置MySQL时…...
如何理解分布式锁?
分布式锁的实现有哪些? 1.Memcached分布式锁 利用Memcached的add命令。此命令是原子操作,只有在key不存在的情况下,才能add成功,也就意味着线程得到了锁。 2.Reids分布式锁 和Memcached的方式类似,利用Redis的setn…...
windows 远程连接 ubuntu桌面xrdp
更新 sudo apt update安装组件 sudo apt-get install xorg sudo apt-get install xserver-xorg-core sudo apt-get install xorgxrdp sudo apt install xfce4 xfce4-goodies xorg dbus-x11 x11-xserver-utilsxrdp sudo apt install xrdp sudo systemctl status xrdp sudo …...
visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
【Veristand】Veristand环境安装教程-Linux RT / Windows
首先声明,此教程是针对Simulink编译模型并导入Veristand中编写的,同时需要注意的是老用户编译可能用的是Veristand Model Framework,那个是历史版本,且NI不会再维护,新版本编译支持为VeriStand Model Generation Suppo…...
ui框架-文件列表展示
ui框架-文件列表展示 介绍 UI框架的文件列表展示组件,可以展示文件夹,支持列表展示和图标展示模式。组件提供了丰富的功能和可配置选项,适用于文件管理、文件上传等场景。 功能特性 支持列表模式和网格模式的切换展示支持文件和文件夹的层…...
客户案例 | 短视频点播企业海外视频加速与成本优化:MediaPackage+Cloudfront 技术重构实践
01技术背景与业务挑战 某短视频点播企业深耕国内用户市场,但其后台应用系统部署于东南亚印尼 IDC 机房。 随着业务规模扩大,传统架构已较难满足当前企业发展的需求,企业面临着三重挑战: ① 业务:国内用户访问海外服…...
数据结构:泰勒展开式:霍纳法则(Horner‘s Rule)
目录 🔍 若用递归计算每一项,会发生什么? Horners Rule(霍纳法则) 第一步:我们从最原始的泰勒公式出发 第二步:从形式上重新观察展开式 🌟 第三步:引出霍纳法则&…...
