当前位置: 首页 > news >正文

【Java16】多态

向上类型转换

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

当一个引用变量的编译时类型和运行时类型不一致时,就出现了多态(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); // 6bc.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; // floatvar 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); // 向上转型,运行时类型是Integervar 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());
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【Cesium开发实战】火灾疏散功能的实现,可设置火源点、疏散路径、疏散人数
  • 修正版头像上传组件
  • 网络规划与设计————期末复习
  • 华为手机联系人不见了怎么恢复?3个解决方案
  • Go协程与通道的综合应用问题
  • 240707-Sphinx配置Pydata-Sphinx-Theme
  • Linux tputs
  • vb.netcad二开自学笔记9:界面之ribbon
  • linux源码安装mysql8.0的小白教程
  • Nginx和Tomcat实现负载均衡群集部署应用
  • k8s record 20240705
  • 视频号矩阵系统源码,实现AI自动生成文案和自动回复私信评论,支持多个短视频平台
  • Android Camera Framework:从基础到高级
  • vue3+springboot+mybatis+mysql项目实践--简单登录注册功能实现
  • seaweedfs + TiKV 部署保姆级教程
  • AzureCon上微软宣布了哪些容器相关的重磅消息
  • chrome扩展demo1-小时钟
  • Docker入门(二) - Dockerfile
  • ESLint简单操作
  • JAVA并发编程--1.基础概念
  • JAVA多线程机制解析-volatilesynchronized
  • Java知识点总结(JavaIO-打印流)
  • JS基础篇--通过JS生成由字母与数字组合的随机字符串
  • Linux CTF 逆向入门
  • Python学习笔记 字符串拼接
  • Spring Boot快速入门(一):Hello Spring Boot
  • SpringBoot几种定时任务的实现方式
  • Vue ES6 Jade Scss Webpack Gulp
  • 百度小程序遇到的问题
  • 猴子数据域名防封接口降低小说被封的风险
  • 使用docker-compose进行多节点部署
  • 数据仓库的几种建模方法
  • 再谈express与koa的对比
  • LevelDB 入门 —— 全面了解 LevelDB 的功能特性
  • ​业务双活的数据切换思路设计(下)
  • #《AI中文版》V3 第 1 章 概述
  • #每日一题合集#牛客JZ23-JZ33
  • #图像处理
  • %3cli%3e连接html页面,html+canvas实现屏幕截取
  • ( )的作用是将计算机中的信息传送给用户,计算机应用基础 吉大15春学期《计算机应用基础》在线作业二及答案...
  • (12)Hive调优——count distinct去重优化
  • (2022 CVPR) Unbiased Teacher v2
  • (web自动化测试+python)1
  • (zz)子曾经曰过:先有司,赦小过,举贤才
  • (附源码)ssm码农论坛 毕业设计 231126
  • (剑指Offer)面试题41:和为s的连续正数序列
  • (十一)图像的罗伯特梯度锐化
  • (未解决)macOS matplotlib 中文是方框
  • (源码版)2024美国大学生数学建模E题财产保险的可持续模型详解思路+具体代码季节性时序预测SARIMA天气预测建模
  • (转)EXC_BREAKPOINT僵尸错误
  • (转)菜鸟学数据库(三)——存储过程
  • .java 指数平滑_转载:二次指数平滑法求预测值的Java代码
  • .NET 8.0 发布到 IIS
  • .NET 设计模式初探
  • .NET/C# 使用 SpanT 为字符串处理提升性能