【Java探索之旅】多态:向上下转型、多态优缺点、构造函数陷阱

文章目录

  • 📑前言
  • 一、向上转型和向下转型
    • 1.1 向上转型
    • 1.2 向下转型
  • 二、多态的优缺点
    • 2.1 多态优点
    • 2.2 多态缺陷
  • 三、避免避免构造方法中调用重写的方法
  • 四、好的习惯
  • 🌤️全篇总结

📑前言

在面向对象编程中,向上转型和向下转型是常用的技术手段,可以实现不同类之间的转换和灵活应用。同时,多态作为面向对象编程的重要特性,具有诸多优点和缺陷,对代码的设计和性能都有一定影响。本文将深入探讨向上转型、向下转型以及多态的优缺点,帮助读者更好地理解和运用这些概念在Java编程中的实际应用和注意事项。

一、向上转型和向下转型

1.1 向上转型

实际就是创建一个子类对象,将其当成父类对象来使用。

语法格式:父类类型 对象名 = new 子类类型()

Animal animal = new Cat(“小小”,3);

animal是父类类型,但可以引用一个子类对象,因为是从小范围向大范围的转换。

使用场景

  1. 直接赋值
  2. 方法传参
  3. 方法返回
class TestAnimal {// 2. 方法传参:形参为父类型引用,可以接收任意子类的对象public static void eatFood(Animal a){a.eat();} // 3. 作返回值:返回任意子类对象public static Animal buyAnimal(String var){if("狗".equals(var) ){return new Dog("狗狗",1);}else if("猫" .equals(var)){return new Cat("猫猫", 1);}else{return null;}}public static void main(String[] args) {// 1. 直接赋值:子类对象赋值给父类对象Animal cat = new Cat("元宝",2); Dog dog = new Dog("小七", 1);eatFood(cat);eatFood(dog);Animal animal = buyAnimal("狗");animal.eat();animal = buyAnimal("猫");animal.eat();}
}

**向上转型的优点:**让代码实现更简单灵活。

**向上转型的缺陷:**不能调用到子类特有的方法。

1.2 向下转型

将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转换。

public static void main(String[] args) {Animal animal1 = new Dog("小七",7);//向上转型Animal animal2 = new Cat("咪咪",3);//向上转型animal1 = (Dog)animal1;//向下转型animal2 = (Cat)animal1;//向下转型编译报错,因为animal1实际上是指向的是Dog,强制转化为Cat无法还原}

向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。Java中为了提高向下转型的安全性,引入了 instanceof ,如果该表达式为true,则可以安全转换。

二、多态的优缺点

2.1 多态优点

  1. 提高代码的可扩展性和可维护性:通过多态,可以将具体的实现与抽象的接口分离,使得系统的各个模块之间的耦合度降低,从而方便对系统进行扩展和维护。
  2. 增强代码的灵活性:通过多态,可以在运行时动态地决定对象的具体类型,从而实现不同对象的不同行为。这样可以根据实际需求灵活地进行对象的选择和使用。
  3. 提高代码的可读性和可理解性:通过多态,可以将对象的具体类型隐藏起来,只关注对象的抽象类型和接口,从而使得代码更加简洁、清晰,易于理解和阅读
  4. 能够降低代码的 “圈复杂度”, 避免使用大量的 if - else

扩展

  • 圈复杂度是一种描述一段代码复杂程度的方式. 一段代码如果平铺直叙, 那么就比较简单容易理解. 而如果有很多的条件分支或者循环语句, 就认为理解起来更复杂.
  • 因此我们可以简单粗暴的计算一段代码中条件语句和循环语句出现的个数, 这个个数就称为 “圈复杂度”。如果一个方法的圈复杂度太高, 就需要考虑重构,不同公司对于代码的圈复杂度的规范不一样. 一般不会超过 10 。
class Book{public void read(){System.out.println("看书");}
}
class English extends Book{public void read(){System.out.println("看英语书");}
}
class Maths extends Book{public void read(){System.out.println("看数学书");}
}
class Language extends Book{@Overridepublic void read() {System.out.println("看语文书");}
}
public class Exercise3 {public static void readBook(){English english = new English();Maths maths = new Maths();Language language = new Language();Book[] book = {english,language,maths,language,maths,english};for (Book x:book) {x.read();}}public static void main(String[] args) {readBook();}
}

2.2 多态缺陷

性能损失:由于多态需要在运行时进行类型的判断和方法的动态绑定,所以会带来一定的性能损失。相比于直接调用具体类型的方法,多态需要进行额外的判断和查找,从而导致一定的性能下降。

可能引发运行时错误:由于多态是在运行时动态决定对象的具体类型,所以如果在使用多态的过程中出现了类型错误或者类型转换错误,就会导致运行时错误的发生。这就需要在使用多态时进行严格的类型检查和错误处理,增加了代码的复杂性和难度。

可能导致代码的混乱和难以理解:多态的使用会使得代码中出现更多的抽象和接口,从而增加了代码的复杂性和难度。如果使用不当,可能会导致代码的混乱和难以理解,降低代码的可读性和可维护性。因此,在使用多态时需要谨慎设计和使用。

三、避免避免构造方法中调用重写的方法

class B {public B() {
// do nothingfunc();}public void func() {System.out.println("B.func()");}
}
class D extends B {private int num = 1;@Overridepublic void func() {System.out.println("D.func() " + num);}
}
public class Test {public static void main(String[] args) {D d = new D();}
}
// 执行结果D.func() 0

一段有坑的代码. 我们创建两个类, B 是父类, D 是子类. D 中重写 func 方法. 并且在 B 的构造方法中调用 func。

构造 D 对象的同时, 会调用 B 的构造方法.

B 的构造方法中调用了 func 方法, 此时会触发动态绑定, 会调用到 D 中的 func

此时 D 对象自身还没有构造, 此时 num 处在未初始化的状态, 值为 0. 如果具备多态性,num的值应该是1.

所以在构造函数内,尽量避免使用实例方法,除了final和private方法。

四、好的习惯

“用尽量简单的方式使对象进入可工作状态”, 尽量不要在构造器中调用方法(如果这个方法被子类重写, 就会触发动态绑定, 但是此时子类对象还没构造完成), 可能会出现一些隐藏的但是又极难发现的问题。

🌤️全篇总结

向上转型可以实现子类对象当成父类对象使用,提高代码的灵活性和复用性;向下转型则可以让父类引用还原为子类对象,调用子类特有方法。多态能够提高代码的可扩展性和可维护性,但也存在性能损失和潜在的错误风险,需要谨慎使用和设计。

避免在构造函数中调用重写方法,保持构造过程简洁和清晰,避免潜在的问题。形成良好的编程习惯,能够提高代码的质量和可维护性,减少潜在的错误和调试成本,是每位Java程序员都应该注意的重要方面。

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

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

相关文章

Docker-11☆ Docker Compose部署RuoYi-Cloud

一、环境准备 1.安装Docker 附:Docker-02-01☆ Docker在线下载安装与配置(linux) 2.安装Docker Compose 附:Docker-10☆ Docker Compose 二、源码下载 若依官网:RuoYi 若依官方网站 鼠标放到"源码地址"上,点击"RuoYi-Cloud 微服务版"。 跳转至G…

Splunk Enterprise 任意文件读取漏洞(CVE-2024-36991)

文章目录 前言漏洞描述影响版本漏洞复现POC批量检测-nuclei脚本 修复建议 前言 Splunk Enterprise 是一款强大的机器数据管理和分析平台,能够实时收集、索引、搜索、分析和可视化来自各种数据源的日志和数据,帮助企业提升运营效率、增强安全性和优化业务…

C++11右值引用及移动构造

区分左值和右值 在学习c11的右值引用前,大家肯定会有点陌生什么是右值?什么是左值?现在我先来带大家熟悉一下概念。 左值 可以被取地址,也可被修改(const修饰的除外) 可以出现在等号左边,也可…

软件架构之开发方法

软件架构之开发方法 第6章:开发方法6.1 软件生命周期6.2 软件开发模型6.2.1 瀑布模型6.2.2 演化模型6.2.3 螺旋模型6.2.4 增量模型6.2.5 构件组装模型 6.3 统一过程6.4 敏捷方法6.4.1 极限编程6.4.2 特征驱动开发6.4.3 Scrum6.4.4 水晶方法6.4.5 其他敏捷方法 6.5 软…

开源模型应用落地-FastAPI-助力模型交互-进阶篇(一)

一、前言 FastAPI 的高级用法可以为开发人员带来许多好处。它能帮助实现更复杂的路由逻辑和参数处理,使应用程序能够处理各种不同的请求场景,提高应用程序的灵活性和可扩展性。 在数据验证和转换方面,高级用法提供了更精细和准确的控制&#…

Java线程的创建·启动和休眠

一.线程的创建和启动 Java中创建线程的两种方式 ◆继承java.lang.Thread类 ◆实现java.lang.Runnable接口 ◆使用线程的步骤 继承Thread类创建线程 ◆自定义线程类继承自Thread类 ◆重写run()方法,编写线程执行体 ◆创建线程对象,调用start()方法启动…

数据驱动制造业升级,免费可视化工具成关键

制造业作为国民经济的支柱产业,正经历着前所未有的变革。数据,作为这场变革的核心驱动力,其重要性不言而喻。然而,面对海量且复杂的数据,如何高效、直观地将其转化为有价值的洞察,成为了众多制造企业亟待解…

前端面试题25(css常用的预处理器)

在前端开发领域,CSS预处理器在面试中经常被提及,其中最流行的三种预处理器是Sass、LESS和Stylus。下面分别介绍它们的特点和优势: 1. Sass(Syntactically Awesome Style Sheets) 优势: 变量:允…

实战Qt开发WordBN笔记软件#01 搭建开发环境:VS2019+Qt6.5+CMake+Git

01 背景 【WordBN字远笔记】是天恩软件工作室开发的一款免费笔记软件;WordBN基于VS2019、Qt6.5开发,使用Qt Quick(QML)开发语言。 本课程将以【WordBN字远笔记】的界面为实战基础,详细介绍如何基于Qt/QML开发语言&am…

从新手到高手:Scala函数式编程完全指南,Scala IF…ELSE 语句(8)

1、Scala IF…ELSE 语句 Scala IF…ELSE 语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块。 可以通过下图来简单了解条件语句的执行过程: 1.1、if 语句 if 语句有布尔表达式及之后的语句块组成。 语法 if 语句的语法格式如下&…

LT7911UX 国产原装 一拖三 edp 转LVDS 可旋转 可缩放

2.一般说明 该LT7911UX是一种高性能Type-C/DP1.4a到MIPI或LVDS芯片的VR/显示应用。HDCP RX作为HDCP转发器的上游,可以与其他芯片的HDCP TX配合实现转发器功能。 对于DP1.4a输入,LT7911UX可配置为1/2/4通道。自适应均衡使其适用于长电缆应用,最…

python对象

类 我们目前所学习的对象都是Python内置的对象但是内置对象并不能满足所有的需求,所以我们在开发中经常需要自定义一些对象类,简单理解它就相当于一个图纸。在程序中我们需要根据类来创建对象类就是对象的图纸!我们也称对象是类的实例&#…

给您介绍工控CAN总线

CAN是什么 CAN,全称Controller Area Network,即控制器局域网,是一种由Bosch公司在1983年开发的通信协议。它主要用于汽车和工业环境中的电子设备之间的通信。CAN协议定义了物理层和数据链路层的通信机制,使得不同的设备能够通过CA…

LabVIEW开发商业软件的多角度分析与注意事项

在使用LabVIEW开发商业软件时,有许多方面需要考虑和注意,包括项目管理、架构设计、性能优化、用户体验、安全性、维护与支持等。以下是从多个角度详细分析在LabVIEW中开发商业软件需要注意的事项。 项目管理 需求分析:确保深入了解客户需求&a…

BFS:边权相同的最短路问题

一、边权相同最短路问题简介 二、迷宫中离入口最近的出口 . - 力扣&#xff08;LeetCode&#xff09; class Solution { public:const int dx[4]{1,-1,0,0};const int dy[4]{0,0,1,-1};int nearestExit(vector<vector<char>>& maze, vector<int>& e…

使用C++实现ATM系统,谈谈思路及代码实现

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

文华财经盘立方期货通鳄鱼指标公式均线交易策略源码

文华财经盘立方期货通鳄鱼指标公式均线交易策略源码&#xff1a; 新建主图幅图类型指标都可以&#xff01; VAR1:(HL)/2; 唇:REF(SMA(VAR1,5,1),3),COLORGREEN; 齿:REF(SMA(VAR1,8,1),5),COLORRED; 颚:REF(SMA(VAR1,13,1),8),COLORBLUE;

Unity实现安卓App预览图片、Pdf文件和视频的一种解决方案

一、问题背景 最近在开发app项目&#xff0c;其中有个需求就是需要在app软件内显示图片、pdf和视频&#xff0c;一开始想的解决方案是分开实现&#xff0c;也就是用Image组件显示图片&#xff0c;找一个加载pdf的插件和播放视频的插件&#xff0c;转念一想觉得太麻烦了&#x…

张量分解(2)——张量运算(内积、外积、直积、范数)

&#x1f345; 写在前面 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;这里是hyk写算法了吗&#xff0c;一枚致力于学习算法和人工智能领域的小菜鸟。 &#x1f50e;个人主页&#xff1a;主页链接&#xff08;欢迎各位大佬光临指导&#xff09; ⭐️近…

vue3中使用 tilwindcss报错 Unknown at rule @tailwindcss

解决方法&#xff1a; vscode中安装插件 Tailwind CSS IntelliSense 在项目中的 .vscode中 settings.json添加 "files.associations": {"*.css": "tailwindcss"}