JavaSE从零开始到精通(六) - 多态

1. 前言

现实中的多态:同一个对象在不同时刻展现出的不同形态!

编程中的多态:

        对于具体的类型和调用的方法,在编程时并不确定,而是在程序运行期间才确定,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。

核心:允许在编译时使用父类类型,而在运行时使用子类类型的实现。

调用成员变量:编译看左边,运行看左边

  1. 编译看左边:左边(父类)中存在该变量编译器不会出现编译时异常
  2. 运行看右边:实际输出的结果是父类的内容

扩展:对于继承中,变量查找顺序,在堆中创建内存的时候,需要有一块存储父类的变量,查找的时候先找父类的,再找子类的。 

调用成员方法:编译看左边,运行看右边  

  1. 编译看左边:左边(父类)中存在该变量编译器不会出现编译时异常
  2. 运行看右边:实际输出的结果是子类重写的内容

多态的作用:消除类型之间的耦合关系。

概述一下上述:实现动态绑定,在执行期间判断所引用对象的实际类型,根据实际的类型调用其相应的方法。

2. 条件和格式

Java 实现多态有 3 个必要条件:继承重写向上转型。 

格式:父类 变量名 = new 子类();

  • 必须在继承体系之下

前者为后者的父类

  • 子类必须对父类当中的方法进行重写

多态性允许不同对象对同一消息做出不同的响应。通过重写父类的方法,子类可以根据自身的特性提供不同的实现。当使用父类引用指向子类对象时,调用重写的方法会调用子类的实现,从而实现多态。

  • 必须通过父类的引用调用重写的方法(向上转型)

可以在编译时,使用范围更大的数据类型。

当使用父类的引用调用方法时,调用方只关心父类定义的接口,而不需要知道具体的子类实现细节。这种设计有助于减少代码间的依赖性。

 下面我将通过两个方面介绍:多态的好处

2. 编译时多态

编译时多态就是我们平常所熟知的重载,也就是方法的参数列表(类型、个数、顺序有一项不同即可)不同。这里其实是广义上的多态的概念,那么从狭义上的多态来理解的话,我们所说的多态其实就是运行时多态,也就是再编译阶段,我们无法确定对象的类型,只有在运行的时候我们才能确定类型,并且调用相应类型的方法。

好处:可以在编译时,使用范围更大的数据类型。

2.1. 使用父类的类型存储子类
public class Test {public static void main(String[] args) {//创建父类数组(更广泛的数据类型)Person[] p = new Person[5];p[0] = new Student();//存进去会进行自动类型转换(向上转型)p[1] = new Teacher();}
}
如果你只使用特定的子类(小的数据范围),绝对无法存储。

举例:我创建一个人可以进入的房间,老师和学生都属于人自然都可以进入。但是我要设置只有老师可以进入,学生自然不能进入。

2.2 参数传参(方法重载)
public class Test2 {public static void main(String[] args) {Student s = new Student();Teacher t = new Teacher();show(s);show(t);}private static void show(Person p){System.out.println(p.getName() + "查看了");}
}

上述方法与下述方法等价:

    private static void show(Student s){System.out.println(s.getName() + "查看了");}private static void show(Teacher t){System.out.println(t.getName() + "查看了");}

 这样看不出是否方便,但是person如果有一百个子类呢,哪个方便就可想而知了。

 举例:一个学生管理平台,有一个登录方法。如果我设置只有老师可以登录(调用登录方法),

学生不乐意了,如果这样学生,老师不乐意了,所以为了灵活性和可扩展性,我可以设置这样是人都可以调用这个方法,所以二者都可以调用登录方法。

public boolean login(Person p) {}

2.3 方法返回值
    // 返回一个随机动物的方法public static Animal getRandomAnimal() {Random random = new Random();if (random.nextBoolean()) {return new Dog(); // 向上转型为 Animal} else {return new Cat(); // 向上转型为 Animal}}

这跟上述一样,要使用更大的范围接受返回值。

3. 运行时多态

运行时多态是指在程序运行时,调用同一个方法名但具体执行的代码可能会根据调用对象的实际类型而有所不同的现象。这是通过Java中的方法重写(Override)和动态绑定(Dynamic Binding)机制实现的。

这就是核心:允许在编译时使用父类类型,而在运行时使用子类类型的实现。

// 动物类
class Animal {public void makeSound() {System.out.println("动物发出了声音");}
}// 狗类,继承自动物类
class Dog extends Animal {@Overridepublic void makeSound() {System.out.println("汪汪汪~");}
}// 猫类,继承自动物类
class Cat extends Animal {@Overridepublic void makeSound() {System.out.println("喵喵喵~");}
}public class Main {public static void main(String[] args) {Animal animal1 = new Dog(); // 向上转型Animal animal2 = new Cat(); // 向上转型animal1.makeSound(); // 运行时调用的是 Dog 类的 makeSound 方法animal2.makeSound(); // 运行时调用的是 Cat 类的 makeSound 方法}
}

在这个示例中:

  • animal1 和 animal2 都被声明为 Animal 类型,但实际上它们分别指向 Dog 和 Cat 类型的对象。
  • 当调用 animal1.makeSound() 和 animal2.makeSound() 时,由于动态绑定,实际调用的是 Dog 类和 Cat 类中重写的 makeSound() 方法。
  • 这里的关键是,在编写 main 方法时,并不需要知道 animal1 和 animal2 分别是 Dog 和 Cat 类的对象,只需知道它们是 Animal 类型的对象即可。

4. 多态的弊端

多态的弊端:不可以访问子类特有的方法。

        即便是向上转型,将子类的引用赋值给父类对象,那么这个父类对象也只能访问子类从父类当中继承来的方法,不可以访问子类当中特有的方法,这是为什么呢?

        因为我们多态的实现,是在编译期间动态绑定实现的,也就是说只有在运行期间才能够确定,父类所引用的子类类型是什么,在编译期间我们只知道,父类的类型是什么,所以这也就导致了,向上转型之后,父类对象也只能访问子类当中从父类继承来的方法,不能够访问子类特有的方法。因为编译器知道父类的类型并不知道,你到底使用的哪个子类,子类的类型需要运行的时候才能知道。不能随便调用,如果传来的是猫,给你调用狗的方法就尴尬了。

解决问题的方案:

针对这个问题,我们有提出了向下转型,这样就可以使得我们父类引用方便地访问我们子类当中特有的方法。 

  向下转型:

把高类型的引用转换成低类型的引用
 格式:             低类型 变量 = (低类型)高类型的变量;

package Yangon;public class Animal {public int age;public String name;public void eat() {System.out.println("吃东西");}
}
class Dog extends Animal{public void eat(){System.out.println("小狗正在吃东西");}public void play(){System.out.println("小狗正在玩耍");}
}
class Bird extends Animal{public void eat(){System.out.println("小鸟正在吃东西");}
}
class Main{public static void main(String[] args) {Animal animal = new Dog();if(animal instanceof Dog){Dog myRealDog = (Dog)animal;myRealDog.play();//这里是可以调用子类的特有方法的,因为对父类进行了强制类型转换//也就是向下转型}Animal animal1_bird= new Bird();if(animal1_bird instanceof Bird){//编译就会报错,不允许将Bird 赋值给 Dog类,这就是instanceof的意义所在Dog bird_dog = (Bird)animal1_bird;}}
}

 对于上述代码使用了,instanceof,下面我将讲解instanceof的作用

5. instanceof

instanceof 是 Java 中的一个关键字,用于检查一个对象是否是一个特定类的实例或者是其子类的实例。其主要作用是进行类型检查,可以用来确定一个对象的实际类型是否与我们期望的类型兼容。

语法格式: object instanceof Class

其中 Object 是要检查的对象,Class 是要检查的类或接口。

作用:

1. 类型检查

  • instanceof 主要用于判断一个对象是否属于某个类的实例,或者是否实现了某个接口。例如:
class Animal {}
class Dog extends Animal {}Animal animal = new Dog();if (animal instanceof Dog) {System.out.println("animal is a Dog");
}
  • 在上述例子中,animal instanceof Dog 返回 true,因为 animal 是 Dog 类的实例。

2. 避免类型转换异常

if (animal instanceof Dog) {Dog dog = (Dog) animal; // 安全地进行类型转换dog.bark();
}

3. 多态性的应用

  • instanceof 在处理多态情况下尤为重要。它允许程序在运行时动态地确定对象的实际类型,从而根据不同类型采取不同的行为。

4. 判断对象是否为 null

  • 使用 instanceof 还可以避免对 null 对象进行操作而导致空指针异常。例如:
if (animal instanceof Dog) {// 对 animal 进行操作,因为 animal 不是 null 且是 Dog 的实例
} else {// animal 是 null 或者不是 Dog 的实例
}

 

总之,instanceof 是 Java 中用于进行类型检查的重要工具,能够帮助开发人员在运行时确定对象的实际类型,从而进行安全的类型转换或者采取适当的行动。

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

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

相关文章

git rebase 和 git merge区别

# 在自己的分支feature上开发,从master上拉最新改动, # 避免自己的分支落后master git checkout feature git rebase master1. 合并方式 merge:合并操作。它会取出一个公共的祖先节点,然后尝试将两个分支从该节点开始发生的所有变化都合并到…

【网络】linux实现双网卡热备:优先走A,故障后走B

本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》:python零基础入门学习 《python运维脚本》: python运维脚本实践 《shell》:shell学习 《terraform》持续更新中:terraform_Aws学习零基础入门到最佳实战 《k8…

vue3 + antd vue 纯前端 基于xlsx 实现导入excel 转 json,将json数据转换XLSX并下载(下载模版)

一、导入 0、关键代码 // 安装插件 npm i xlsx/yarn add xlsx // 导入xlsx import * as XLSX from xlsx; 点击提交的时候才整理数据。上传的时候文件保存在 state.form.file[0] 中的 // 定义字段映射关系 const fieldMap {sheet2json: {技能名称: skill_name,技能等级: …

uni-app 影视类小程序开发从零到一 | 开源项目分享

引言 在数字娱乐时代,移动设备已成为我们生活中不可或缺的一部分,尤其是对于电影爱好者而言,随时随地享受精彩影片成为一种日常需求。爱影家,一款基于 uni-app 开发的影视类小程序,正是为此而生。它不仅提供了丰富的影…

栈的概念—函数调用

也是一块内存空间,CPU的SP寄存器指向它,可用于函数调用、局部变量、多任务系统里保存现场 每个任务都有自己的栈

【Django+Vue3 线上教育平台项目实战】购物车与订单模块的精简实现与数据安全策略

文章目录 前言一、购物车模块1.后端核心逻辑2.前端页面代码3.操作流程及演示 二、订单模块1.订单模块模型类设计1.展示订单信息a.页面展示b.前端核心代码c.后端核心逻辑 2.订单是否使用优惠券与积分a.页面展示b.前端核心代码 3.订单支付方式a.页面展示b.前端核心代码 4.提交订单…

Perl脚本的魔法:打造自定义文件系统视图

Perl脚本的魔法:打造自定义文件系统视图 在文件管理与自动化任务中,Perl以其强大的文本处理能力而闻名。通过Perl,我们可以轻松实现自定义的文件系统视图,以满足特定的需求。这不仅可以帮助我们更好地组织和访问文件,…

PyTorch Autograd内部实现

原文: 克補 爆炸篇 25s (youtube.com) 必应视频 (bing.com)https://www.bing.com/videos/riverview/relatedvideo?&qPyTorchautograd&qpvtPyTorchautograd&mid1B8AD76943EFADD541E01B8AD76943EFADD541E0&&FORMVRDGAR 前面只要有一个node的re…

北京交通大学《深度学习》专业课,实验3卷积、空洞卷积、残差神经网络实验

一、实验要求 1. 二维卷积实验(平台课与专业课要求相同) ⚫ 手写二维卷积的实现,并在至少一个数据集上进行实验,从训练时间、预测精 度、Loss变化等角度分析实验结果(最好使用图表展示) ⚫ 使用torch.nn…

Matlab基础语法篇(下)

Matlab基础语法(下) 一、逻辑基础(一)逻辑运算符(二)all、any、find函数(三)练习 二、结构基础(一)条件结构(1)if-elseif-else-end&am…

Android构建任务assemble、bundle、compile、package、install

1. assemble 开头的任务: assembleDebug:构建 debug 版本的 APK 文件。assembleRelease:构建 release 版本的 APK 文件。assembleAndroidTest:构建测试 APK 文件,用于测试应用程序。assembleAndroidTestDebug&#xf…

十、操作符详解

目录 1、操作符分类 2、二进制转换 2.1二进制转十进制 2.1.1、十进制转二进制 2.2、二进制转八进制和十六进制 2.2.1、二进制转八进制 2.2.2、二进制转十六进制 3、原码、反码、补码 4、移位操作符(移动的是二进制位) 4.1、左移操作符 4.2、右…

VMware虚拟机下安装Ubuntu(详细教程,最小系统的安装,含VMware Tools)

1.VM的下载安装 VMware的下载安装教程_vm16 pro下载-CSDN博客 2. Ubuntu 下载 在官网或者镜像站下载所需版本的.ios镜像,这个镜像在接下来的步骤中会用到: Ubuntu 22.04.4 LTS 下载 和 清华大学开源软件镜像站 - Ubuntu 22.04.4 下载 3. 创建虚拟机 […

【C语言】深入解析希尔排序

文章目录 什么是希尔排序?希尔排序的基本实现代码解释希尔排序的优化希尔排序的性能分析希尔排序的实际应用结论 在C语言编程中,希尔排序是一种高效的排序算法,是插入排序的一种更高效的改进版本。它通过比较相距一定间隔的元素来进行排序&am…

智能水果保鲜度检测:基于YOLO和深度学习的完整实现

引言 水果新鲜程度直接影响其口感和营养价值。为了提高水果品质管理的效率和准确性,本文介绍了一种基于深度学习的水果新鲜程度检测系统。该系统包括用户界面,利用YOLO(You Only Look Once)v8/v7/v6/v5模型进行水果新鲜程度检测&…

RocketMQ中概念知识点记录 和 与SpringBoot集成实现发送 同步、异步、延时、批量、tag、key、事务消息等

1. 消息模型 消息(Message): 是 RocketMQ 中数据传输的基本单位,由主题、标签、键值、消息体等组成。主题(Topic): 消息的分类,类似于邮件的主题,用于对消息进行粗粒度的分类。标签&#xff08…

Zookeeper是什么,为什么要用,怎么用?

关于Zookeeper的全面了解与应用 前言:这几天在开发过程中,遇到了zk相关的一些问题,大体先复习下 Zookeeper作为分布式系统中的协调服务,起着至关重要的角色。本篇文章将从以下几个方面详细讲解什么是Zookeeper,为什么…

【Go系列】RPC和grpc

承上启下 介绍完了Go怎么实现RESTFul api,不可避免的,今天必须得整一下rpc这个概念。rpc是什么呢,很多人都想把rpc和http一起对比,但是他们不是一个概念。RPC是一种思想,可以基于tcp,可以基于udp也可以基于…

【STM32嵌入式系统设计与开发---拓展】——1_10矩阵按键

这里写目录标题 1、矩阵按键2、代码片段分析 1、矩阵按键 通过将4x4矩阵按键的每一行依次设为低电平,同时保持其它行为高电平,然后读取所有列的电平状态,可以检测到哪个按键被按下。如果某列变为低电平,说明对应行和列的按键被按下…

Android焦点之FocusWindow切换流程

关键调用是setInputWindows InputDispatcher::dispatchFocusLocked:在这里打印日志"Focus entering" 或 "Focus leaving" SurfaceFlinger::updateInputFlingernotifyWindowInfos();mWindowInfosListenerInvoker->windowInfosChanged(windo…