在多态的方法调用中为什么会出现“左边编译左边运行”的现象?多态创建的对象到底是谁属于父类还是子类?通过深扒集合remove方法调用理解其原理

news2024/9/25 10:41:18

目录

“左边编译左边运行”的两个原因:

什么是“编译看左边,运行看右边”?

为什么会出现“左边编译左边运行”现象?

1. 子类没有重写父类的方法

2. 重载与重写的混淆(重难点)

问题:编译器是怎么看一个方法是重写还是重载的呢?

区分方式:查看方法的签名

如何避免“左边编译左边运行”的限制?

拓展:多态创建的对象到底是谁属于父类还是子类?

1. 对象本质属于子类

2. 引用属于父类

3. 编译时看引用类型,运行时看对象类型

编译时:

运行时:

4. 多态性不改变对象的实际类型

5. 总结

总结


在 Java 的多态机制中,“编译看左边,运行看右边” 是一个非常常见的规则,它描述了 Java 在编译时和运行时对方法调用的不同处理方式。然而,有时候我们会遇到一种情况,即使对象的实际类型是子类,编译器依然只允许调用父类的方法,这种现象就是所谓的左边编译左边运行。本文将详细解释这种现象及其背后的原因。

“左边编译左边运行”的两个原因:

  1. 子类没有重写父类的方法
  2. 重载与重写的混淆

知道了原因后,我们下面来进行进一步的深挖。


什么是“编译看左边,运行看右边”?

在 Java 中,多态允许我们使用父类的引用指向子类对象。这种机制下,方法的调用行为可以用“编译看左边,运行看右边”来描述:

  • 编译时看左边:编译器根据变量的声明类型(即左边的类型)来确定哪些方法是合法的。换句话说,编译器会检查父类或接口中是否存在要调用的方法,如果存在,编译通过。(看看父类或者接口中有没有调用的方法)

  • 运行时看右边:在程序运行时,实际的对象类型(即右边的类型)决定了具体执行哪个版本的方法。也就是说,如果子类重写了父类的方法,运行时将执行子类的重写版本。(如果父类中有该方法则执行子类重写的该方法)

然而,有时我们会遇到一种似乎只看“左边”的情况,也就是所谓的“左边编译左边运行”。让我们深入了解这种现象。


为什么会出现“左边编译左边运行”现象?

尽管“编译看左边,运行看右边”是多态的核心原则,但在某些情况下,我们确实会看到编译器似乎只根据左边的类型来限制方法的调用。这种现象主要发生在以下两种情况下(上面已经提及过):

子类没有重写父类方法、重载与重写的混淆

1. 子类没有重写父类的方法

在多态中,当我们通过父类的引用调用一个方法时,如果子类没有重写父类中的该方法,编译器只能调用父类的方法。这种情况导致即使子类对象在运行时被赋给父类引用,程序依然会执行父类中的方法,而不会调用子类中的逻辑。

示例

class Animal {
    public void makeSound() {
        System.out.println("Animal makes sound");
    }
}
​
class Dog extends Animal {
    // Dog 没有重写 makeSound 方法
}
​
public class Main {
    public static void main(String[] args) {
        Animal myDog = new Dog();  // 父类引用指向子类对象
        myDog.makeSound();  // 输出:Animal makes sound
    }
}

在这个例子中:

  • myDog 的实际对象类型是 Dog,但因为 Dog 没有重写 Animal 类中的 makeSound() 方法,所以调用的仍然是父类 AnimalmakeSound() 方法。

  • 编译时:编译器会根据变量的声明类型 Animal 检查 makeSound() 方法,并确定可以调用。

  • 运行时:由于子类 Dog 没有重写 makeSound(),因此即使 myDog 实际是 Dog 类型,程序仍然会调用 Animal 的实现。这就是所谓的“左边编译左边运行”现象。

2. 重载与重写的混淆(重难点)

在多态场景下,方法重写(Override)方法重载(Overload)的行为有所不同:

  • 重写:子类重写父类的方法,编译时只要父类中定义了这个方法,编译就会通过。运行时,程序会根据子类对象来调用重写后的版本。

  • 重载:重载是指在同一个类中定义多个同名但参数不同的方法。在多态情况下,编译器只会根据变量的编译时类型来决定调用哪个重载版本。如果编译时类型没有匹配的重载方法,编译会报错。

示例

Collection<String> list = new ArrayList<>();
list.remove(1);  // 编译错误,Collection 接口中没有 remove(int index) 方法

在这个例子中,Collection 接口中只定义了 remove(Object o),而 ArrayList 类重载了这个方法,添加了 remove(int index)。由于编译器只看 Collection 的方法集,无法识别 remove(int index),因此会报编译错误。即便实际对象是 ArrayList,编译器依然不会允许调用 remove(int index),因为它“看不到”这个方法。

问题:编译器是怎么看一个方法是重写还是重载的呢?

那么问题来了!我们注意到在ArrayList的源代码中有两个remove方法,编译器是怎么看一个方法是重写还是重载的呢?

ArrayList 中,我们确实看到了两个 remove 方法:

  • remove(Object o):根据对象删除元素。

  • remove(int index):根据索引删除元素。

这两个方法名字相同,但参数类型不同,所以它们是方法重载(overloading)的例子。但是其中的

  • remove(Object o) 是对 Collection 接口中定义的 remove(Object o) 方法的重写
  • remove(int index)ArrayList 特有的重载方法。

为了验证上述说法,我们还可以看一下父类接口中的remove方法源代码:

即便没有 @Override 注解,我们依然可以通过以下方式区分哪个是重写,哪个是重载。

区分方式:查看方法的签名

重写(Override)

  • 重写发生在子类中,方法的签名必须与父类或接口中的方法完全一致

  • 方法的签名包括:方法名、参数类型、参数顺序、返回类型

ArrayList 中的 remove(Object o) 方法,签名与 Collection 接口中的 remove(Object o) 完全一致:

// Collection 接口中的定义
boolean remove(Object o);
  • 方法名:remove

  • 参数类型:Object

  • 返回类型:boolean

ArrayList 中的 remove(Object o) 的实现与这个签名完全一致,因此它是对 Collection 接口的重写,而剩下同名的方法则是remove方法的重载


如何避免“左边编译左边运行”的限制?

为了避免编译时的限制,你可以通过类型转换来解决问题。通过强制类型转换,编译器会认识到你正在处理子类类型,从而允许调用子类特有的方法。

示例

Animal myDog = new Dog();
((Dog) myDog).fetch();  // 合法,强制转换为 Dog 类型后可以调用 fetch

这种转换告诉编译器:你确实要调用子类的方法,从而避免编译时的类型限制。

拓展:多态创建的对象到底是谁属于父类还是子类?

在 Java 中,当我们谈论多态时,我们常常会使用父类的引用指向子类的对象。这个时候,多态生成的对象实际上是子类的实例(运行看子类),但它通过父类的引用(编译看父类)进行访问和操作。

让我们通过分解几个关键点,来明确这个问题:

1. 对象本质属于子类

在多态情况下,尽管我们使用父类的引用去指向对象,但这个对象的实际类型是子类的实例。换句话说,无论引用的类型是什么,对象始终是子类的实例,并且它具有子类的所有特性和行为。

示例:

Animal myDog = new Dog();  // 父类引用指向子类对象

在这段代码中:

  • myDog 是一个 Animal 类型的引用,但它指向了 Dog 类的实例。

  • 实际对象Dog,即使引用类型是 Animal,这个对象本质上属于子类 Dog

2. 引用属于父类

尽管对象是子类的实例,但在多态情况下,我们使用的是父类的引用类型。在编译时,Java 编译器只知道这个引用是父类类型,因此它只允许我们调用父类中定义的方法和属性。

示例:

myDog.makeSound();  // 调用的是 Dog 的 makeSound 方法
  • 在编译时,编译器检查到 Animal 类中有 makeSound() 方法,因此允许调用。

  • 运行时,由于 myDog 实际上是 Dog 的实例,执行的是 Dog 类的 makeSound() 方法(如果 Dog 重写了 makeSound())。

  • 但是属性是调用父类的,运行也是使用父类的属性值

3. 编译时看引用类型,运行时看对象类型

这就是 Java 中多态的关键:编译时根据引用类型检查方法的可用性,运行时根据实际对象类型执行相应的行为

编译时:
  • 编译器只根据父类的引用类型(左边)来检查哪些方法可以调用,即使对象是子类实例,编译器也不会允许调用子类特有的方法(除非子类重写了父类的方法)

运行时:
  • 程序在运行时会根据实际的子类对象来决定执行哪个版本的方法。如果子类重写了父类的方法,调用的将是子类的实现。

4. 多态性不改变对象的实际类型

多态本质上是通过父类引用来操作子类对象的机制,但它不会改变对象的实际类型。对象仍然是子类的实例,拥有子类的所有特性和行为。例如,即使你使用父类的引用,你仍然可以通过类型转换访问子类特有的方法。

示例:

Animal myDog = new Dog();
// myDog.fetch();  // 编译错误,Animal 没有 fetch 方法
​
// 需要类型转换来调用子类的特有方法
((Dog) myDog).fetch();  // 合法,调用 Dog 类的 fetch 方法
  • myDog 实际上是 Dog 类型的对象,只不过它被一个 Animal 类型的引用所引用。

  • 如果你希望调用子类的特有方法,需要进行强制类型转换,这表明对象的本质仍然是子类对象。

5. 总结

  • 多态生成的对象本质上属于子类。即使通过父类的引用来访问,该对象始终是子类的实例,并且拥有子类的所有属性和方法(包括重写的方法)。

  • 引用属于父类。在编译时,编译器只能看到父类中的方法和属性,并且只能允许调用这些方法。

  • 运行时根据子类对象执行行为。即使引用是父类类型,程序在运行时会调用子类中重写的方法。

因此,在多态中,对象始终属于子类,但我们通过父类引用来控制和操作它。

说白了就是编译器只能调用父类中的方法和属性,除非子类中对父类的方法进行了重写或者进行强制类型转换


总结

Java 多态中的“编译看左边,运行看右边”原则强调了编译时和运行时的行为差异。在大多数情况下,方法的调用是根据变量的编译时类型检查的,而实际的执行依赖于运行时对象的类型。然而,当父类或接口中没有子类特有的方法,或者遇到重载方法时,编译器只看左边的类型,导致“左边编译左边运行”现象。

理解这一现象有助于我们更好地运用 Java 的多态机制,并知道何时以及如何使用类型转换来解决编译时的限制问题!

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

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

相关文章

JAVA开源项目 体育馆管理系统 计算机毕业设计

本文项目编号 T 048 &#xff0c;文末自助获取源码 \color{red}{T048&#xff0c;文末自助获取源码} T048&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析5.4 用例设计 六、核…

每日论文1——应用于65nm CMOS锁相环完全电流匹配的电荷泵

《A Charge Pump with Perfect Current Matching Applied to Phase-Locked Loop in 65nm CMOS》2021 IEEE 14th International Conference on ASIC 电荷泵PLL的结构框图如图&#xff0c;其中CP的充放电电流不匹配会引起PLL的频率误差和杂散。 传统的电荷泵结构在输出处的电平…

强烈推荐的10款企业文件加密软件|2024企业办公文件加密

随着信息安全威胁的不断增加&#xff0c;企业文件加密成为保护敏感数据的重要手段。在2024年&#xff0c;有多款文件加密软件可供选择&#xff0c;帮助企业提高数据安全性。以下是十款强烈推荐的企业文件加密软件。 1.安秉加密软件 安秉加密软件专为企业设计&#xff0c;主要用…

【Web】初识Web和Tomcat服务器

目录 前言 一、认识web 1. 软件架构模式 2. web资源 3. URL请求路径&#xff08;统一资源定位符&#xff09; 二、Tomcat服务器 1. 简介 2. tomcat服务器的目录结构 3.使用tomcat服务器启动失败的常见原因 3.1 端口冲突 3.2 jdk环境变量配置出错 三、使用Tomcat发布…

OpenSSH9.7升级至OpenSSH9.9(openssh7以后所有版本升级均可使用该方法)

1、查看当前openssh版本&#xff0c;使用命令ssh -V 2、开启当前主机的telnet和ftp service xinetd start #开启telnetservice vsftpd start #开启ftp 3、下载openssh最新版 下载地址: OPENSSH下载地址https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/ 4、开始安…

【Redis入门到精通五】Java如何像使用MySQL一样使用Redis(jedis安装及使用)

目录 Jedis 1.jedis是什么 2.jedis的安装配置 3.jedis的基础命令操作展示 1.set和get操作&#xff1a; 2.exists和del操作&#xff1a; 3.keys和type操作&#xff1a; 4. expire和ttl&#xff1a; Jedis Java 操作 redis 的客⼾端有很多&#xff0c;其中最知名的是 jedi…

大模型备案和互联网算法备案的区别?

最近&#xff0c;接到很多客户的电话咨询大模型备案和互联网算法备案&#xff0c;好多人搞不清楚这两个有什么关系&#xff1f;有什么区别&#xff1f;我们先来看下全国大模型备案和互联网算法备案通过的情况是怎么样的&#xff1f; 截至写稿之时&#xff0c;全国通过大模型备…

2024最新最全【Qubes Linux系统安装下载】零基础入门到精通!

Qubes Linux的安装和设置过程非常简单。它也是一个以安全为中心的桌面操作系统&#xff0c;旨在通过隔离提供安全性&#xff0c;这对于系统管理员、记者和有道德的黑客来说是一个很好的卖点。 Qubes是一个使用Xen的 Linux 发行版&#xff0c;这是一种虚拟化技术&#xff0c;它…

卫星图片地面目标识别检测数据集 1500张 yolo数据集 已增强

卫星图像地面目标识别数据集&#xff08;Satellite Image Ground Target Recognition Dataset, SIGTRD&#xff09; 摘要 SIGTRD 是一个专门为卫星图像中的地面目标识别而设计的数据集&#xff0c;它包含了一系列常见的基础设施和交通工具类型。该数据集提供了1500张卫星图像…

基于单片机的指纹打卡系统

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于STC89C52RC&#xff0c;采用两个按键替代指纹&#xff0c;一个按键按下&#xff0c;LCD12864显示比对成功&#xff0c;则 采用ULN2003驱动步进电机转动&#xff0c;表示开门&#xff0c;另一个…

电脑桌面归纳小窗口如何设置?电脑桌面一键整理工具分享!

电脑桌面归纳小窗口如何设置&#xff1f;日常使用电脑的过程中&#xff0c;随着文件、应用程序的不断增加&#xff0c;桌面往往会变得杂乱无章&#xff0c;这不仅影响了美观&#xff0c;也降低了工作效率。幸运的是&#xff0c;现代技术为我们提供了多种桌面整理工具&#xff0…

【QA-MISRA】解决使用命令行扫描项目后看不到报告的问题

1、 文档目标 解决使用命令行扫描项目后看不到报告的问题 2、 问题场景 客户使用命令行扫描项目后看不到报告&#xff0c;原因是客户未设置和勾选报告格式就导出了DAX文件进行命令行直接扫描。 3、软硬件环境 1、软件版本&#xff1a; QA-MISRA23.04 2、机器环境&#xff1…

李宏毅2023机器学习作业HW07解析和代码分享

ML2023Spring - HW7 相关信息&#xff1a; 课程主页 课程视频 Kaggle link 回来了 : ) Sample code HW07 视频 HW07 PDF 个人完整代码分享: GitHub | Gitee | GitCode P.S. HW7 的代码都很易懂&#xff0c;可以和 2024 年的新课&#xff1a;生成式AI导论做一个很好的衔接&#…

开源 AI 智能名片与 S2B2C 商城小程序:嫁接权威实现信任与增长

摘要&#xff1a;本文探讨了嫁接权威在产品营销中的重要性&#xff0c;并结合开源 AI 智能名片与 S2B2C 商城小程序&#xff0c;阐述了如何通过与权威关联来建立客户信任&#xff0c;提升产品竞争力。强调了在当今商业环境中&#xff0c;巧妙运用嫁接权威的方法&#xff0c;能够…

一款前后端分离设计的企业级快速开发平台,支持单体服务与微服务之间灵活切换(附源码)

前言 当前软件开发面临诸多挑战&#xff0c;诸如开发效率低下、重复工作多、维护成-本高等问题&#xff0c;这些问题在一定程度上阻碍了项目的进展。针对这些痛点&#xff0c;我们迫切需要一款既能提升开发效率又能降低维护成-本的处理方案。由此&#xff0c;一款基于前后端分…

HDMI20协议解析_Audio_Clock_Regeneration

HDMI20协议解析_Audio_Clock_Regeneration 1.版本说明 日期作者版本说明20240918风释雪初始版本 2.概述 当通过HDMI传输音频信号时&#xff0c;Audio Clock Regeneration&#xff08;ACR&#xff09;是必须要传输的数据包之一&#xff1b; HDMI传输过程中&#xff0c;音频采样…

数学建模-线性规划讲解(Matlab版本)

引言 相信不少小伙伴刚开始接触数学建模时&#xff0c;第一个学习的算法就是运筹学的重要分支--数学规划&#xff0c;而数学规划当中重要的分支就是线性规划了。在这里笔者参考了司守奎和孙玺菁老师的《数学建模算法与应用》(第三版&#xff09;这本书&#xff0c;以此来讲讲关…

同等学力申硕英语多少分及格

同等学力申硕全国统考与往年的分数线一样&#xff0c;英语、学科综合均为60分合格通过制&#xff0c;满分均100分。 单科分数未达到及格线的考生&#xff0c;次年5月可以参加单科的补考 同等学力申硕的意义和作用 授予同等学力人员硕士学位是国家为同等学力人员开辟的获得学位…

前端——阿里图标的使用

阿里图标 将小图标定义成字体&#xff0c;通过引入字体的方式来展示这些图标 1.打开阿里图标库 https://www.iconfont.cn/ 2.登录 / 注册一个账号 3.选中你需要使用的图标 并且把它加入购物车 4.全部选择完之后 点击右上角 购物车 然后下载代码 5.解压后你下载的文…

MySQL数据库的日志你知道几个?

1、前言 MySQL相信大家都用过&#xff0c;但MySQL中都有哪些日志&#xff0c;是干什么的&#xff0c;估计有小伙伴还没有搞清楚。可能有小伙伴只知道最重要的三个&#xff1a;undolog、redolog、binlog。其实这是不全的&#xff0c;MySQL中的日志有&#xff1a; undolog&…