JAVA SE(9)——多态

1.多态的概念&作用

多态(Polymorphism)是面向对象编程的三大基本特性之一(封装和继承已经讲过了),它允许不同类的对象对同一消息做出不同的响应。具体来说,多态允许基类/父类的引用指向派生类/子类的对象(向上转型),并通过该引用调用子类中重写的方法,从而实现不同的行为

2.实现多态的条件

在Java中,要实现多态必须满足以下条件,缺一不可:

  • 1.在继承体系下
  • 2.父类引用指向子类对象(向上转型)
  • 3.子类必须对父类中的可重写方法进行重写
  • 4.通过父类引用调用子类中重写的方法

在实现多态之前,我们必须先搞清楚向上转型和重写是什么

3.向上转型

3.1 概念

向上转型是面向对象编程中的一种类型转换机制,它允许将子类的对象引用转换为父类的引用。这种类型转换是安全的,隐式的

3.2 语法格式

父类类型 变量名 = new 子类对象

public class Animal {public int age;public String name;
}
public class Dog extends Animal {public String color;
}
public class Test {public static void main(String[] args) {Animal dog = new Dog();}
}

在上述代码中,Dog类继承自Animal类,在实例化Dog对象的时候,创建了一个父类类型的引用(变量)来指向该对象,这就是向上转型,下面我画个图来表示父类引用和子类对象在内存中的关系图:
在这里插入图片描述
实现向上转型有三种方式:
(1)直接赋值:

Animal dog = new Dog();

(2)方法传参:

public class Test {public static void func(Animal a) {a.eat();}public static void main(String[] args) {Dog dog = new Dog();func(dog);}
}

(3)方法返回值:

public class Test {public static Animal func(String value) {if (value.equals("cat")) {return new Cat();}else if (value.equals("dog")) {return new Dog();}else {return null;}}public static void main(String[] args) {Animal animal = func("cat");}
}

注意:当发生向上转型后,父类引用无法访问子类中原有的属性和方法,只能访问从父类中继承而来的属性和方法,也就是说父类引用的访问范围如下:
在这里插入图片描述
那么,在已经发生向上转型后,如何才能访问子类原有的属性和方法呢?
有两个办法:

  • 1.向下转型
  • 2.使用多态性

4.向下转型

4.1 如何使用向下转型

通过显式地将父类引用转换为子类引用,可以访问子类原有的属性和访问

public class Animal {public int age;public String name;
}
public class Dog extends Animal {public String color;
}
public class Test {public static void main(String[] args) {//向上转型Animal dog = new Dog();System.out.println(dog.age = 10);System.out.println(dog.name = "Dog");//向下转型Dog dog1 = (Dog) dog;System.out.println(dog1.color = "白色");}
}

允许结果:
10
Dog
白色
在这里插入图片描述

4.2 向下转型存在的风险

注意:
向下转型在**编译**时是允许的,但如果没有正确地检查对象的实际类型,运行时可能会抛出ClassCastException异常。这是因为父类引用可能实际上引用的是父类本身或其他子类的对象,而不是目标子类的对象

public class Animal {public int age;public String name;
}
//Dog类继承Animal
public class Dog extends Animal {public String color;
}
//Cat类继承Animal
public class Cat extends Animal {public int weight;
}
public class Test {public static void main(String[] args) {//向上转型Animal dog = new Dog();System.out.println(dog.age = 10);System.out.println(dog.name = "Dog");//向下转型Dog dog1 = (Dog) dog;System.out.println(dog1.color = "白色");//错误的向下转型Cat cat = (Cat) dog;cat.weight = 10;}
}

上述代码中,Dog和Cat都继承自Animal,但是在发生向下转型的时候,使用Cat类型的引用指向Dog对象。这在编译时是不会报错的,因为Cat和Dog同属于Animal的子类,但是当程序运行之后就会抛出ClassCastException异常

因为向下转型本质上是强制类型转换,将Animal dog引用(指向Dog对象的引用)强转为Dog类型是允许的,因为Dog类型的引用指向Dog对象很合理;但是,如果将Animal dog引用(指向Dog对象的引用)强转为Cat类型,这是不允许的,因为Cat类型的引用无法指向Dog对象。这就相当于无法将boolean类型强转为int类型

4.3 instanceof运算符

如果用户错误地使用向下转型,在编译阶段是不容易被察觉出来的,只有运行阶段才会报错。如果程序运行起来之后才发现错误,可能已经带来了损失,所以为了规避这一情况,Java引入了instanceof运算符来帮助用户检测错误

public class Animal {public int age;public String name;
}
//Dog类继承Animal
public class Dog extends Animal {public String color;
}
//Cat类继承Animal
public class Cat extends Animal {public int weight;
}
public class Test {public static void main(String[] args) {//向上转型Animal dog = new Dog();System.out.println(dog.age = 10);System.out.println(dog.name = "Dog");//向下转型if (dog instanceof Dog) {//dog instanceof Dog 为true//说明该向下转型是安全的Dog dog1 = (Dog) dog;}if (dog instanceof Cat) {//dog instanceof Cat 为false//说明该向下转型是不安全的,不执行if语句中的代码Cat cat = (Cat) dog;}}
}

5.重写

5.1 概念

**重写(Override)**是面向对象编程中的一个重要概念,它允许子类提供一个与父类中已定义的方法具有相同名称、参数列表和返回类型的方法。重写使得子类能够改变或扩展父类方法的实现

5.2 语法格式

public class Animal {public int age;public String name;//父类的eat方法public Animal eat() {System.out.println("Animal is eating.");return null;}
}
public class Dog extends Animal {public String color;//重写父类的eat方法@Overridepublic Dog eat() {System.out.println("Dog is eating.");return null;}
}

重写的规则:

  • 1.子类重写父类的方法时,必须和父类的方法名、参数列表保持一致
  • 2.返回值类型可以不一样,但必须具有父子关系。上述代码中,子类重写的方法的返回值类型可以是被重写方法返回值类型的子类
  • 3.子类中重写方法的访问权限必须大于等于父类中被重写的方法
  • 4.父类中被static、final、private修饰的方法以及构造方法不能被重写
  • 5.重写方法是可以借助@Override注解。虽然这个注解不影响方法的实现逻辑,但是可以帮助我们进行合法性检查。比如:如果不小心将方法名写错了(写成了ate),注解就会帮我们报错
  • 6.重写只针对方法,和属性/变量无关

5.3 重写和重载的区别

特性重载(Overload)重写(Override)
定义在同一个类中,方法名相同但参数列表不同在子类中重新定义一个与父类中已定义的方法具有相同签名、返回值一样或这具有父子关系的方法
目的提供方法的不同实现,以适应不同的参数类型和数量改变或扩展父类方法的实现
访问限定修饰符无要求子类方法的访问权限必须大于等于父类方法
调用时机编译时根据参数列表来确定调用哪个方法运行时确定
关键字无关键字可以借助@Override注解

5.4 动态绑定

上面讲向上转型时讲过,当父类引用指向子类对象时,该父类引用只能访问子类中继承自父类的属性和方法。那么,当向上转型和方法重写同时发生时,会碰撞出怎么样的火花呢?

public class Animal {public int age;public String name;//父类的eat方法public Animal eat() {System.out.println("Animal is eating.");return null;}
}
public class Dog extends Animal {public String color;//重写父类的eat方法@Overridepublic Dog eat() {System.out.println("Dog is eating.");return null;}
}
public class Test {public static void main(String[] args) {Animal dog = new Dog();dog.eat();}
}

在上述代码中,引用指向子类的对象,再通过该父类引用去调用父类中被重写的eat方法,按照我们之前学习的知识,此时运行结果应该是:Animal is eating.,但实际上:

运行结果:
Dog is eating.

这里可以得出一个结论:当父类引用去调用父类中被重写的方法时,真正被调用的方法是子类中重写的方法。
注意:

  • 1.具体最终调用哪个方法,在编译时无法确定,在运行时根据对象的实际类型来确定
    在这里插入图片描述
    在编译时认为调用Animal中的eat方法,但是根据运行结果来看,实际上调用的是子类中重写的eat方法
  • 2.在运行时确定调用的具体方法,这称之为动态绑定/后期绑定,也是实现多态的基础

6. 多态

6.1 多态的具体实现

public class Animal {public int age;public String name;//父类的eat方法public Animal eat() {System.out.println("Animal is eating.");return null;}
}
public class Dog extends Animal {public String color;//重写父类的eat方法@Overridepublic Dog eat() {System.out.println("Dog is eating.");return null;}
}
public class Cat extends Animal {public int weight;//重写父类的eat方法@Overridepublic Cat eat() {System.out.println("Cat is eating.");return null;}
}
public class Test {public static void func(Animal animal) {animal.eat();}public static void main(String[] args) {Dog dog = new Dog();Cat cat = new Cat();func(dog);func(cat);}
}

运行结果:
Dog is eating.
Cat is eating.

这里我们再回顾一下多态的概念:多态允许基类/父类的引用指向派生类/子类的对象(向上转型),并通过该引用调用子类中重写的方法,从而实现不同的行为。

在上述func方法中,同样是通过animal形参来调用eat方法,两个运行的结果发生了变化,这个过程就叫做多态

6.2 使用多态降低圈复杂度

什么叫圈复杂度?
圈复杂度是一种描述一段代码复杂程度的方式。如果一段代码平铺直叙,那么就比较容易理解;如果一段代码使用很多的条件分支或者循环语句,就认为代码理解起来比较复杂。

我们可以简单地举个例子,一层if-else语句表示一圈复杂度,如果一段代码的圈复杂度太高,就需要考虑重新构建该代码的结果。一般来说,圈复杂度不应该超过10。
现在我们需要打印多个图形

public class Shape {public void draw() {System.out.println("Shape");}
}
public class Flower extends Shape {@Overridepublic void draw() {System.out.println("flower");}
}
public class Rect extends Shape{@Overridepublic void draw() {System.out.println("rect");}
}
public class Circle extends Shape {@Overridepublic void draw() {System.out.println("circle");}
}

下面是不使用多态的代码:

public class Test {public static void main(String[] args) {String[] array = new String[]{"flower","rect","circle"};//for(String cur : array) {if(cur.equals("flower")) {new Flower().draw();}else if(cur.equals("rect")) {new Rect().draw();}else {new Circle().draw();}}}
}

下面是使用多态的代码:

public class Test {public static void main(String[] args) {Shape[] array = new Shape[]{new Flower(), new Rect(), new Circle()};for(Shape cur : array) {cur.draw();}}
}

6.3 避免在构造方法中调用重写的方法

在这里插入图片描述

实际执行结果:
Son0
期望执行结果:
Son10

上述代码中,在父类的构造方法中调用func方法,此时调用的是子类中重写的func方法。在子类的func方法中访问了实例成员变量age,但是此时age还没有开始初始化。因为此时仍然处于父类的构造方法中,而子类的实例成员变量初始化发生在父类构造方法结束之后。解决办法有两个:

  • 1.将age变量使用static修饰(不建议)
  • 2.避免在构造方法中调用重写的方法(建议)

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

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

相关文章

GPS定位方案

目录 一、常用的GPS定位方案包括: 二、主流品牌及热销型号 三、常用GPS算法及核心逻辑: 一、基础定位算法 二、高精度算法 三、辅助优化算法 四、信号处理底层算法 四、基本原理(想自己写算法的琢磨一下原理) 一、常用的GP…

PCIe - ZCU106(RC) + KU5P(EP) + 固化

目录 1. 简介 1.1 Data Mover 1.2 描述符 2. ZCU102 2.1 Ubuntu OS 2.2 USB Host 2.2.1 连接拓扑 2.2.2 设备类型 2.2.3 USB 跳帽设置 2.3 无线网卡 2.4 PCIe Info 2.4.1 Diagram 2.4.2 lspci -tv 2.4.3 lspci -v 2.4.2.1 设备基本信息 2.4.2.2 控制与状态寄存…

精益数据分析(43/126):媒体网站商业模式的盈利与指标解析

精益数据分析(43/126):媒体网站商业模式的盈利与指标解析 在创业和数据分析的学习旅程中,我们不断探索各种商业模式的奥秘,今天让我们一同深入《精益数据分析》,聚焦媒体网站商业模式,剖析其盈…

Android数据库全栈开发实战:Room+SQLCipher+Hilt企业级应用构建

简介 在移动应用开发中,数据库作为数据存储的核心组件,其安全性和性能对企业级应用至关重要。本文将从零开始,全面讲解Android数据库开发的最新技术,包括Room框架的深度使用、SQLCipher加密数据库的实现、Hilt依赖注入的集成以及前后端数据同步的完整方案。通过一个加密任…

HarmonyOS 5.0 低时延音视频开发​​

大家好,我是 V 哥。 在HarmonyOS 5.0的开发中,支持低时延音视频开发,为了确保语法正确, V 哥以下代码符合HarmonyOS NEXT API 14的规范。为了方便初学者更好入门,V 哥伙同2位小伙伴花了1年时间,搞了三本鸿蒙…

微调大模型如何准备数据集——常用数据集,Alpaca和ShareGPT

微调大模型如何准备数据集——常用数据集,Alpaca和ShareGPT 数据集准备常用数据集自定义数据集AlpacaShareGPT数据集准备 常用数据集 预训练数据集 Wiki Demo (en)RefinedWeb (en)RedPajama V2 (en)Wikipedia (en)Wikipedia (zh)Pile (en)

2025年OpenAI重大架构调整:资本与使命的再平衡

目录 前言 一、调整核心:三重架构的重构 1.1 控制权的重新锚定 1.2 营利部门的角色转型 1.3 资金池的重新配置 二、调整动因:三重矛盾的破解 2.1 资金需求与融资限制的冲突 2.2 商业竞争与使命纯度的博弈 2.3 内部治理与外部监管的张力 三、产…

GD32/STM32 ADC/DMA使用指南

首先我们对ADC及DMA的基础知识作一下简单介绍。 一、 GD32/STM32 ADC模块的核心要点 一)、ADC基础特性 ‌12位逐次逼近型‌ GD32/STM32 ADC为12位分辨率,最大量化值为4095(对应3.3V参考电压),支持0-3.3V模拟输入范…

Linux(十四)进程间通信(IPC),管道

一、进程间通信 (一)系统介绍进程间通信 进程间通信(IPC)介绍 小编插入的这篇文章详细介绍了进程间通信的一些内容,大家可以一起学习。 (二)进程间通信的方法 1、管道 2、信号量 3、共享…

使用Homebrew下载配置git和连接GitHub(Mac版)

本文详细介绍了在M系列Mac上安装Homebrew并配置Git的过程,包括git的下载、设置全局用户名和邮箱、生成SSH密钥、添加GitHubSSH密钥以及终端验证。这些步骤有助于用户顺利进行协同开发。 一、下载git 1、终端输入一下命令 brew install git2、这时下载完成 二、配…

悬崖边的摄影牧歌

在云雾缭绕的悬崖边,一场独属于自然与光影的邂逅悄然上演。 摄影师伫立于此,身旁是一群灵动的羊。他架起相机,眼神专注而炽热,仿佛要将这天地间的一切都收纳进小小的取景器。悬崖陡峭,岩石冷峻,却因羊群的洁…

Linux环境基础与开发工具使用

1. Linux编译器vim 1.1 vim的基本概念讲解 vim有很多种模式,我们初学者常用的就是命令模式(command mode)、插入模式(Insert mode)和底行模式(last line mode)。 命令/正常模式(Normal mode) …

《Python星球日记》 第36天:线性代数基础

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏:《Python星球日记》,限时特价订阅中ing 目录 一、标量、…

使用VMware Workstation pro 17.5.1在Windows上安装Ubuntu 24.04.2的 详细步骤

一、准备工作 1. 下载Ubuntu 24.04.2 ISO镜像 官方下载地址:Ubuntu 24.04.2 (Noble Numbat) 选择 ubuntu-24.04.2-desktop-amd64.iso(桌面版)或 ubuntu-24.04.2-live-server-amd64.iso(服务器版)。 2. 确认系统要求…

ios systeam introduction

Here is an in-depth look at Apple’s iOS, from its inception to its latest major release, covering architecture, core components, security, app lifecycle, development tools, and the headline features of iOS 18. iOS began life as “iPhone OS,” unveiled alo…

优化04-选择率和直方图

选择率 在Oracle数据库中,选择率(Selectivity) 是优化器(CBO,基于成本的优化器)用来评估SQL语句中某个条件(如WHERE子句)过滤数据的比例的关键指标。它直接影响优化器选择执行计划的…

python实战:通过输入文字匹配在docx文档中的具体位置

在指定的docx文档中,输入一串文字来查看该文字在文档中的具体位置;方便后续处理(如替换文字,高亮显示等等操作) from docx import Documentdef find_text_in_docx(file_path, search_text):# 读取docx文件doc = Document(file_path)# 遍历段落,查找匹配的文本for i

Flutter——数据库Drift开发详细教程(二)

目录 1.核心API1.1查询数据列表分页1.2 列表排序1.3推迟获取与观察 1.核心API 1.1查询数据列表分页 限制返回的结果数量limit&#xff0c;从某一位置开始查询offset ///limit10, offset10 Future<List<TodoItem>> limitTodos(int limit, {int? offset}) {return …

mux-vlan基础配置

1.top配置 2.各个交换机设置 sw3交换机的 sysname swb # undo info-center enable # vlan batch 10 20 30 100 # vlan 10description financial vlan vlan 20description marketing vlan vlan 30description client vlan vlan 100description principal vlanmux-vlansubordi…

SAM详解2(初级应用)

SAM SAM5. 初级应用5.1 静态本质不同子串个数5.2 字符串匹配5.3 关于子串出现次数5.4 动态添加时本质不同子串个数SAM 5. 初级应用 记 l o n g e s t ( x ) longest(x) longest(x) 为点 x x x 代表子串集合中最长串的长度。记 s h o r t e s t ( x ) shortest(x) shortest(…