【Java16】多态

news2024/11/13 9:37:50
向上类型转换

对于引用变量,在程序中有两种形态:一种是编译时类型,这种引用变量的类型在声明它的时候就决定了;另一种则是运行时类型,这种变量的类型由实际赋给它的对象决定。

当一个引用变量的编译时类型和运行时类型不一致时,就出现了多态(Polymorphism)

对面向对象语言来说,所有的对象(Object),或者说类的实例,本质上都是引用变量。因此,多态最主要就是针对对象来说的:声明时引用变量指向的对象的类型,和运行时引用变量指向的对象的类型不一致。

在这里插入图片描述

class BaseClass
{
  public int book = 6;
  public void base()
  {
    System.out.println("父类的普通方法");
  }
  public void test()
  {
    System.out.println("父类被覆盖的方法");
  }
}

public class SubClass extends BaseClass
{
  // 覆盖
  public String book = "Java疯狂讲义"; // 同名实例
  public void test()
  {
    System.out.println("子类的覆盖父类的方法");
  }
  public void sub()
  {
    System.out.println("子类的普通方法");
  }
  public static void main(String[] args)
  {
    var bc = new BaseClass(); // 声明一个BaseClass的对象,编译时和运行时的类型一致,不存在多态
    System.out.println(bc.book); // 6
    bc.base(); // 父类的方法
    bc.test(); // 父类的方法
    //-------------
    var sc = new SubClass(); // 声明一个SubClass的对象,同样不存在多态
    System.out.println(sc.book); // 子类实例变量覆盖了父类的实例变量,输出"Java疯狂讲义"
    sc.base(); // 子类方法覆盖了父类的方法
    sc.test(); // 子类的普通方法
    //-------------
    BaseClass polymophicBC = new SubClass(); // 编译时类型是BaseClass,运行时类型是SubClass,发生了多态
    System.out.println(polymophicBC.book); // 输出6,是父类的实例变量
    polymophicBC.base(); // 执行父类的base方法
    polymophicBC.test(); // 执行当前类,也就是运行时类型SubClass的test方法
    // polymophicBC的编译时类型是BaseClass,没有提供sub方法
    // 因此调用sub方法时会出现编译错误
    // polymophicBC.sub();
  }
}
  • 28~31行是标准的对象的声明与使用;
  • 33~36行是标准的继承;
  • 38~41行出现了多态。

对变量polymophicBC来说,编译时类型是BaseClass(声明语句左端),运行时类型是SubClass(声明语句右端)。

把一个子类对象赋给一个父类引用变量,在这个过程中发生了什么?

类型转换

多态在Java中实现的机制就是把子类对象赋值给父类引用变量,这实际上就是一种类型装换,具体也叫向上转型(up-casting)。这种类型转换由系统自动完成。

从声明语句左边来看:

  • BaseClass bc = new BaseClass();
  • BaseClass polymophicBC = new SubClass();

bcpolymophicBC都是BaseClass引用类型,但是它俩在执行同名函数test()时却产生了不同的结果。这种调用同一个方法却出现不同行为特征的现象,就是多态

多态机制下,父类引用变量在运行时总是调用子类的方法,也就是说呈现出子类的行为特征而不是父类的行为特征。

对象的实例变量不具有多态性。

  • 第39行,book仍然是父类的实例变量。

引用变量在编译阶段只能调用其编译时类型拥有的方法,但是在运行时可以执行其运行时类型拥有的方法。

  • 第44行,BaseClass不具有sub()方法,因此不能调用,发生编译错误;
  • 但第41行,BaseClass具有test()方法,因此可以调用,且在运行时执行的是SubClass的同名方法。

使用var时,并不能改变编译时类型,因此也可能会发生多态:

var v1 = new SubClass(); // 自动推断是SubClass,没有多态
var v2 = polymophicBC(); // 赋值,v2自动推断是BaseClass
// 此时调用sub方法,遵照多态机制,会发生编译错误
// v2.sub();
强制类型转换

按照上面规则,引用变量只能调用编译时类型拥有的方法,即使它的运行时类型对象实际上包含了远不止这些方法。

如何让这个引用变量调用运行时类型所拥有的方法呢?

既然普通的多态依赖的是向上转型,即把子类对象赋给父类引用变量,类似于我们把double基本变量赋给float。那么也可以反过来,执行强制类型转换

强制类型转换借助类型转换运算符,和C++类似,就是()

类型转换运算符可以实现基本类型之间的转型,也能实现引用变量的转型。

请注意,强制类型转换不是万能的,受到如下约束:

  • 基本类型之间转型只能在数值类型中进行(整数型、字符型、浮点型)。数值型和布尔型之间不能转换(C++中是可以的)。
  • 引用类型转换只能在具有继承关系(直接继承或间接继承都行)的类型之间进行。

强制类型转换在这里,就是把父类实例转换为子类类型。即其编译时类型是父类类型,运行时类型是子类类型。这时候可以使用强制类型转换。

public class ConversionTest
{
  public static void main(String[] args)
  {
    var d = 13.4; // float
    var l = (long) d; // 强制类型转换
    //------
    var in = 5; // int
    // var b = (boolean) in; // 错误,数值型不能转换为布尔型
    //------
    Object obj = "Hello"; // 向上转型,"Hello"是String,是Object的子类。这实际上就是多态,只不过这时候obj不能执行String拥有的方法
    var objStr = (String) obj; // 强制类型转换,父类/基类和子类,正常
    System.out.println(objStr); // 做为String类型输出
    //------
    Object objPri = Integer.valueOf(5); // 向上转型,运行时类型是Integer
    var in = (Integer) objPri; // 强制类型转换,基类和子类,正常
    // var str = (String) objPri; // objPri运行时时Integer,和String不存在继承关系,运行时会报错(类型转换异常,ClassCastException)
  }
}

再解读一下第12行:

  • objStr,虽然使用了var,但由于使用了强制类型转换符(String),自动推断它是String类型;
  • 此时objObject类型;
  • 因此,将obj赋值给objStr,实际上是把父类对象赋值给子类引用变量,这就和之前的upcasting正好相反,我们也可以称之为downcasting
小结
  1. 把子类对象(右)赋给父类引用变量(左)时,触发向上转型,这种转型是自动的、总是成功的。这种转型表明这个引用变量编译时是父类类型,运行时是子类类型。它表现出的是子类的行为方式,但是编译时不能调用子类的方法。同时,实例变量仍然是父类的。
  2. 使用强制类型转换可以把一个引用变量转换成其子类类型。这种转换必须是显式的,而且不一定成功(若两端不存在继承关系)。
instanceof

使用instanceof运算符可以判断是否可以执行类型转换,以避免出现ClassCastException

if (objPri instanceof String)
{
  var str = (String) objPri;
}

instanceof用来判断前面的对象是否是后面的类或者其子类的实例,是的话返回true,否则返回false

在Java 17中,为instanceof增加了快捷用法,来简化上面的判断代码块:

// 传统instanceof,先判断,再转换,最后使用
if (obj instanceof String) // 先判断
{
  var s = (String) obj; // 再转换
  System.out.println(s.toUpperCase()); // 最后使用
}

// Java 17的模式匹配,同时完成判断和类型转换
if (obj instanceof String s)
{
  System.out.println(s.toUpperCase());
}

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

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

相关文章

mybatis日志记录方案

首先对指定表进行监控 对表进行监控,那么就要使用的是statementInterceptor 拦截器 使用拦截器那么就要写intercepts写拦截条件进行拦截 监控只对与增删改 查询不进行监控 对于字段的监控,是谁修改了字段,那么就进行报警,或者提醒 消息提醒使用钉钉机器人进行消息提醒 P…

AGI 之 【Hugging Face】 的【文本摘要】的 [评估PEGASUS ] / [ 微调PEGASUS ] / [生成对话摘要] 的简单整理

AGI 之 【Hugging Face】 的【文本摘要】的 [评估PEGASUS ] / [ 微调PEGASUS ] / [生成对话摘要] 的简单整理 目录 AGI 之 【Hugging Face】 的【文本摘要】的 [评估PEGASUS ] / [ 微调PEGASUS ] / [生成对话摘要] 的简单整理 一、简单介绍 二、文本摘要 三、在CNN/Daily…

【接口设计】如何设计统一 RESTful 风格的数据接口

如何设计统一 RESTful 风格的数据接口 1.版本控制1.1 通过 URL1.2 通过自定义请求头1.3 通过 Accept 标头 2.过滤信息3.确定 HTTP 的方法4.确定 HTTP 的返回状态5.定义统一返回的格式 近年来,随着移动互联网的发展,各种类型的客户端层出不穷。如果不统一…

347. 前 K 个高频元素(中等)

347. 前 K 个高频元素 1. 题目描述2.详细题解3.代码实现3.1 Python3.2 Java 1. 题目描述 题目中转:347. 前 K 个高频元素 2.详细题解 寻找出现频率前 k k k高的元素,因此需要先统计各个元素出现的次数,该步骤时间复杂度为 O ( n ) O(n) O(n)…

javaweb中的请求与响应--基于postman工具的应用(附带postman的详细安装步骤)

一、前言 后端的第一天感觉难度就上来了,可能是基础太过薄弱了吧。目前看视频已经有点跟不上了,果然15天想要拿下还是太勉强了点。30天还差不多。不知道读者们有没有好好的去学这方面的知识,没有什么是学不会的,关键是坚持。 Po…

【C语言】C语言-学生籍贯信息记录系统(源码+论文)【独一无二】

👉博__主👈:米码收割机 👉技__能👈:C/Python语言 👉公众号👈:测试开发自动化【获取源码商业合作】 👉荣__誉👈:阿里云博客专家博主、5…

C#Winform窗体中嵌入exe文件

1,效果以嵌入Modbus Slave为例: 2,代码: public partial class Form1 : Form{//设置嵌入exe的常量private const int nIndex -16;private const int dwNewLong 0x10000000;Process m_AppProcess;public Form1(){InitializeCompo…

了解AsyncRotationController

概述 基于android 15.0, 以从强制横屏App上滑退回桌面流程来分析 frameworks/base/services/core/java/com/android/server/wm/AsyncRotationController.javaAsyncRotationController 是一种控制器,用于处理设备显示屏旋转时非活动窗口的异步更新。这种控制器通过…

【记录】CSS|Tailwind 的主题定义的颜色的使用方法(--color啥的)

文章目录 【记录】CSS|Tailwind 的主题定义的颜色的使用方法(--color 啥的)省流版GPT 详细解释版Tailwind CSS 配置文件示例使用自定义颜色定义 CSS 变量总结 附赠个 Tips 【记录】CSS|Tailwind 的主题定义的颜色的使用方法&#…

【Python】已解决:ModuleNotFoundError: No module named ‘sklearn.cross_validation

文章目录 一、问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 (机器学习分割数据问题)解决“ModuleNotFoundError: No module named ‘sklearn.cross_validation’” 一、问题背景 在机器学习的实践中,数据分割是…

聚鼎装饰画:现在做装饰画能不能相信

在艺术的殿堂中,装饰画以其多变的风格和独特的魅力占据了一席之地。它们或清新淡雅,或浓烈奔放,总能为现代家居带来一丝生气与美感。然而,在这美丽的背后,却隐藏着一个令人困惑的问题:现在做装饰画&#xf…

[EasilyOpenJCL] 使用 Java 调用显卡 计算 和Java调用 CPU 进行计算 的基准测试!

设备环境介绍 easily-openJCL 是一个轻量级的 Java 语言下的 GPU 显卡 计算库,它提供了一套简单易用的 API,让用户能够轻松实现 GPU 计算操作。 通过 Java 调用 GPU 计算的一个库,使用非常简单的API就可以轻松应付 Java 数据类型在 GPU 中的…

程序使用多进程,打包.exe后,程序陷入死循环

最近写了一个深度学习程序,用cxfreezee打包exe后,在本地运行突然出现死循环,明明在pycharm运行一切正常。 排查了问题,怀疑是多进程的原因,解决办法: 在你的主程序前添加一行代码: if __name_…

Typescript 模块小知识-global scope

问题表现 在编写ts代码的时候遇到一个问题, 表现为, 如果在某个ts工程中, 如果多个文件里面没有任何导出export或者是export default, 那么这些文件如果有const或者是let定义相同的声明都会报错如下 无法重新声明块范围变量 a/a.ts 和 index.ts 和 index2.ts 都没有进行expor…

C++系列-Vector(一)

🌈个人主页:羽晨同学 💫个人格言:“成为自己未来的主人~” Vector的介绍及使用 Vector的介绍 当vector构建的参数类型为char类型时,它是和string是极其类似的,但是二者之间也有不同,比如&#xff0c…

人工智能时代,零基础学IT,我首推Python作为你编程入门语言!

人工智能时代为什么将 Python 称为第一语言? 因为python适应了人工智能时代: 人工智能时代对于代码的简便性有很大要求,像传统的C/CPP/Java学习较为复杂,学习路线长,对于很多零基础的人入门困难。python的兼容性&…

24下软考高级-系统架构设计师100条知识点速记!

11月系统架构设计师现在准备真的太早了吗? 不不不~对于0基础和打工人,留给我们备考的时间其实已经不多了! 想开始不晓得从哪里开始?这里给大家整理了24下系统架构设计师知识点100条,符合最新版教材和考试大纲&#x…

Python实现动态迷宫生成:自动生成迷宫的动画

文章目录 引言准备工作前置条件 代码实现与解析导入必要的库初始化Pygame定义迷宫生成类主循环 完整代码 引言 迷宫生成算法在游戏开发和图形学中有着广泛的应用。它不仅可以用于创建迷宫游戏,还可以用于生成有趣的图案。在这篇博客中,我们将使用Python…

目前哪些充电宝是最强?曝光几款好用的充电宝排行榜

充电宝在我们的日常生活中能为我们带来无与伦比的便捷与体验。然而,当充电宝市场品牌和型号众多,功能丰富多样,口碑最佳的充电宝究竟有哪些?怎样才能挑选到口碑上佳、契合个人需求的充电宝,这已然成为消费者面临的难题…

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【HMAC(C/C++)】

HMAC(C/C) HMAC是密钥相关的哈希运算消息认证码(Hash-based Message Authentication Code),是一种基于Hash函数和密钥进行消息认证的方法。 在CMake脚本中链接相关动态库 target_link_libraries(entry PUBLIC libhuks_ndk.z.so)开发步骤 生…