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

【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程序员都应该注意的重要方面。

相关文章:

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

文章目录 📑前言一、向上转型和向下转型1.1 向上转型1.2 向下转型 二、多态的优缺点2.1 多态优点2.2 多态缺陷 三、避免避免构造方法中调用重写的方法四、好的习惯🌤️全篇总结 📑前言 在面向对象编程中,向上转型和向下转型是常用…...

Linux上web服务器搭建(Apache、Nginx)

第五章 web服务器 第一节 DNS:对域名进行解析,查询对应的地址 1.1 web服务器简介 www是world wide web的缩写,也就是全球信息广播的意思 1.2.网址及HTTP简介 web服务器提供的这些数据大部分都是文件,那么我们需要在服务器端…...

Django QuerySet对象,exclude()方法

模型参考上一章内容: Django QuerySet对象,filter()方法-CSDN博客 exclude()方法,用于排除符合条件的数据。 1,添加视图函数 Test/app11/views.py from django.shortcuts import render from .models import Postdef index(re…...

Qt/C++音视频开发78-获取本地摄像头支持的分辨率/帧率/格式等信息/mjpeg/yuyv/h264

一、前言 上一篇文章讲到用ffmpeg命令方式执行打印到日志输出,可以拿到本地摄像头设备信息,顺藤摸瓜,发现可以通过执行 ffmpeg -f dshow -list_options true -i video“Webcam” 命令获取指定摄像头设备的分辨率帧率格式等信息,会…...

Go bufio包

bufio包: 带缓冲的I/O操作, 减少系统调用次数, 读取文件、网络数据。 bufio包 是什么 bufio 包是 Go 标准库中的一个非常有用的包,用于提供带缓冲的 I/O 操作。它通过缓冲来提高读取和写入的效率,可以有效减少系统调用…...

C++ 类和对象 拷贝构造函数

一 拷贝构造函数的概念: 拷贝构造函数是一种特殊的构造函数,用于创建一个对象是另一个对象的副本。当需要用一个已存在的对象来初始化一个新对象时,或者将对象传递给函数或从函数返回对象时,会调用拷贝构造函数。 二 拷贝构造函…...

C# —— Math对象

Math 数学类 提供了一些相关数学计算的属性和方法、四舍五入、向上求整、向下求整、开平方,几次方 最大值和最小值 sin cos 绝对值 方法 1.Math 常用的字段 Math.PI double x 2 * 180 / Math.PI; Console.WriteLine(x); 2 Math.Abs() 求绝对值 int a -3; Con…...

Face_recognition实现人脸识别

这里写自定义目录标题 欢迎使用Markdown编辑器一、安装人脸识别库face_recognition1.1 安装cmake1.2 安装dlib库1.3 安装face_recognition 二、3个常用的人脸识别案例2.1 识别并绘制人脸框2.2 提取并绘制人脸关键点2.3 人脸匹配及标注 欢迎使用Markdown编辑器 本文基于face_re…...

1-3分钟爆款视频素材在哪找啊?这9个热门爆款素材网站分享给你

在如今快节奏的时代,短视频已成为吸引观众注意力的黄金手段。然而,要制作出1-3分钟的爆款视频,除了创意和剪辑技巧外,选择合适的素材至关重要。那么,哪里可以找到那些能让你的视频脱颖而出的爆款素材呢?不用…...

武汉免费 【FPGA实战训练】 Vivado入门与设计师资课程

一.背景介绍 当今高度数字化和智能化的工业领域,对高效、灵活且可靠的技术解决方案的需求日益迫切。随着工业 4.0 时代的到来,工业生产过程正经历着前所未有的变革,从传统的机械化、自动化逐步迈向智能化和信息化。在这一背景下&…...

【vite创建项目】

搭建vue3tsvitepinia框架 一、安装vite并创建项目1、用vite构建项目2、配置vite3、找不到模块 “path“ 或其相对应的类型声明。 二、安装element-plus1、安装element-plus2、引入框架 三、安装sass sass-loader1、安装sass 四、安装vue-router-next 路由1、安装vue-router42搭…...

最优化方法 运筹学【】

1.无约束 常用公式 线搜索准则:求步长 精确线搜索(argmin) 最速下降:sd:线性收敛 2.算法 SD dk:付梯度-g newton dk:Gkd-g 二阶收敛,步长为1 阻尼牛顿:步长用先搜…...

探索 WebKit 的动感世界:设备方向和运动支持全解析

探索 WebKit 的动感世界:设备方向和运动支持全解析 随着移动设备的普及,网页应用对设备方向和运动的感知需求日益增长。WebKit 作为众多流行移动浏览器的渲染引擎,提供了对设备方向和运动的全面支持,使得 Web 应用能够根据设备的…...

高考假期预习指南

IT专业入门,高考假期预习指南 对于希望进入IT行业的学生来说,假期是学习信息技术的最佳时机。 在信息化快速发展的时代,IT行业的发展前景广阔,但高技能要求使新生可能感到迷茫。 建议新生制定详细的学习计划,包括了解…...

Spring Boot 事件监听机制工作原理

前言: 我们知道在 Spring 、Spring Boot 的启动源码中都大量的使用了事件监听机制,也就是我们说的的监听器,监听器的实现基于观察者模式,也就是我们所说的发布订阅模式,这种模式可以在一定程度上实现代码的解耦&#…...

【AI大模型】驱动的未来:穿戴设备如何革新血液、皮肤检测与营养健康管理

文章目录 1. 引言2. 现状与挑战3. AI大模型与穿戴设备概述4. 数据采集与预处理4.1 数据集成与增强4.2 数据清洗与异常检测 5. 模型架构与训练5.1 高级模型架构5.2 模型训练与调优 6. 个性化营养建议系统6.1 营养建议生成优化6.2 用户反馈与系统优化 7. 关键血液成分与健康状况评…...

【FFmpeg】avcodec_open2函数

目录 1. avcodec_open21.1 编解码器的预初始化(ff_encode_preinit & ff_decode_preinit)1.2 编解码器的初始化(init)1.3 释放编解码器(ff_codec_close) FFmpeg相关记录: 示例工程&#xff…...

matlab:对带参数a关于x的方程求解

题目 讲解 简洁对各个式子的内部含义用浅显易懂的话语总结出来了,耐心体会 f(a) (x)exp(x)x^ax^(sqrt(x))-100;%因为下面的fzero的第一个数需要一个fun,所以这里有两个句柄,第一个a是输入的,第二个x是需要被解出的 A0:0.1:2;%创…...

Yolov10训练,转化onnx,推理

yolov10对于大目标的效果好,小目标不好 一、如果你训练过yolov5,yolov8,的话那么你可以直接用之前的环境就行 目录 一、如果你训练过yolov5,yolov8,的话那么你可以直接用之前的环境就行 二、配置好后就可以配置文件…...

GEE代码实例教程详解:洪水灾害监测

简介 在本篇博客中,我们将使用Google Earth Engine (GEE) 进行洪水灾害监测。通过分析Sentinel-1雷达数据,我们可以识别特定时间段内的洪水变化情况。 背景知识 Sentinel-1数据集 Sentinel-1是欧洲空间局提供的雷达卫星数据集,它能够提供…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

浅谈不同二分算法的查找情况

二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况&#xf…...

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...

从面试角度回答Android中ContentProvider启动原理

Android中ContentProvider原理的面试角度解析,分为​​已启动​​和​​未启动​​两种场景: 一、ContentProvider已启动的情况 1. ​​核心流程​​ ​​触发条件​​:当其他组件(如Activity、Service)通过ContentR…...

图解JavaScript原型:原型链及其分析 | JavaScript图解

​​ 忽略该图的细节(如内存地址值没有用二进制) 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么:保存在堆中一块区域,同时在栈中有一块区域保存其在堆中的地址(也就是我们通常说的该变量指向谁&…...

React从基础入门到高级实战:React 实战项目 - 项目五:微前端与模块化架构

React 实战项目:微前端与模块化架构 欢迎来到 React 开发教程专栏 的第 30 篇!在前 29 篇文章中,我们从 React 的基础概念逐步深入到高级技巧,涵盖了组件设计、状态管理、路由配置、性能优化和企业级应用等核心内容。这一次&…...