Java多态:多态多态,多么变态

news2025/2/23 20:09:54

在这里插入图片描述

  • 👑专栏内容:Java
  • ⛪个人主页:子夜的星的主页
  • 💕座右铭:前路未远,步履不停

目录

  • 一、重写
    • 1、重写的规则
    • 2、重写与重载的区别
  • 二、多态
    • 1、多态的概念
    • 2、多态的实现
    • 3、向上转移和向下转型
      • Ⅰ、向上转型
      • Ⅱ、向下转型
    • 4、多态的优缺点
      • Ⅰ、多态的优点
      • Ⅱ、多态的缺点


嗨!在学习Java的继承之后,让我们进一步深入了解多态的概念吧。多态多态,多么变态。哦不,多态,多态,多种形态。

在继承的基础上,多态通过允许一个对象以多种形态呈现,使得我们可以使用父类的引用来引用子类的对象。这样一来,同样的方法调用可能会在不同的对象上产生不同的行为,具体执行的方法取决于实际对象的类型。通过多态,我们可以编写更加通用、灵活的代码,减少重复性的工作,并且更容易适应未来的需求变化。在实践中,多态常常与抽象类和接口一起使用,以达到更高的代码可复用性和扩展性。

让我们一起深入学习多态的原理和应用,进一步提升我们在Java编程中的技能吧!

在这里插入图片描述

一、重写

在介绍多态之前,先介绍一下什么是重写。重写也称为覆盖,是子类对父类非静态、非private修饰,非final修饰,非构造方法的实现过程进行重新编写,返回值和形参都不能改变。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为,也就是说子类可以根据需要重新实现父类的方法。

1、重写的规则

● 子类在重写父类的方法时,一般必须与父类方法原型一致: 返回值类型 方法名 (参数列表) 要完全一致
● 被重写的方法返回值类型可以不同,但是必须是具有父子关系的
● 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方法就不能声明为 protected
● 父类被staticprivate修饰的方法、构造方法都不能被重写。
● 重写的方法, 可以使用 @Override 注解来显式指定。有了这个注解能帮我们进行一些合法性校验。例如不小心将方法名字拼写错了 (比如写成aet),那么此时编译器就会发现父类中没有 aet 方法,就会编译报错,提示无法构成重写。

2、重写与重载的区别

方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。

特点重写(Override)重载(Overload)
参数列表一定不能修改必须修改
返回类型一定不能修改(除非可以构成父子类关系)可以修改
访问限定符一定不能做更严格的限制(可以降低限制)可以修改

在这里插入图片描述

二、多态

1、多态的概念

多态,多态,多种形态。具体点说就是,当不同的对象被用于执行某个特定行为时,由于它们具有不同的实现,这将导致产生不同的状态。 简单点说就是,同一件事情,发生在不同对象身上,就会产生不同的结果。

我们可以举个举个简单的例子,同样是动物吃饭这个动作。对于小猫来说,就是吃猫粮,对于小狗来说,就是吃狗粮。

在这里插入图片描述

2、多态的实现

在Java中要实现多态,必须要满足如下几个条件,缺一不可:

  • 必须在继承体系下
  • 子类必须要对父类中方法进行重写
  • 通过父类的引用调用重写的方法

多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。

public class Animal { // 定义动物类
  String name; // 动物名
  int age; // 年龄

  public Animal(String name, int age){ // 构造方法,传入动物名和年龄
    this.name = name;
    this.age = age;
  }

  public void eat(){ // 动物吃饭方法
    System.out.println(name + "吃饭");
  }
}

public class Cat extends Animal{ // 猫类继承于动物类
  public Cat(String name, int age){ // 构造方法,传入猫名和年龄,并调用父类的构造方法
    super(name, age);
  }

  @Override // 重写父类的吃饭方法
  public void eat(){
    System.out.println(name+"吃鱼~~~");
  }
}

public class Dog extends Animal { // 狗类继承于动物类
  public Dog(String name, int age){ // 构造方法,传入狗名和年龄,并调用父类的构造方法
    super(name, age);
  }

  @Override // 重写父类的吃饭方法
  public void eat(){
    System.out.println(name+"吃骨头~~~");
  }
}

public class Main {
    // 定义一个静态方法,接收 Animal 类型的形参
    // 编译时并不知道形参引用的具体子类对象,只有运行时才知道
    // 形参类型必须是 Animal,因为 Cat 和 Dog 都是 Animal 的子类
    public static void eat (Animal animal){
        animal.eat();
    }
    // 调用 Animal 类中的 eat 方法,此处的 eat 方法被定义为了 abstract,子类必须实现它
    // 编译时使用形参类型,运行时使用具体对象类型
    public static void main(String[] args) {
    // 创建 Cat 和 Dog 的实例
        Cat cat = new Cat("来福",3);
        Dog dog = new Dog("旺财",9);
    // 调用 eat 方法传入 Cat 和 Dog 类型的实例
    // 编译时根据传入的实例类型推断调用哪个 eat 方法,运行时再调用具体的 eat 方法
        eat(cat);
        eat(dog);
    }
}

在这里插入图片描述

在这里插入图片描述

3、向上转移和向下转型

Ⅰ、向上转型

向上转型是指将一个子类类型的对象引用赋值给一个父类类型的引用变量。这种转型可以在继承关系中进行,其中子类是父类的一个特殊类型。通过向上转型,你可以使用父类的引用来引用子类的对象,从而以一种通用的方式处理不同类型的对象。

简单点说,就是创建一个子类对象,将其当成父类对象来使用。其语法格式:父类类型 对象名 = new 子类类型()

例如:

Animal animal = new Cat("元宝",2);

这里的animal是父类类型,但可以引用一个子类对象,因为是从小范围向大范围的转换
在这里插入图片描述
使用场景:直接赋值、方法传参、方法返回

public 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) {
        Animal cat = new Cat("元宝",2); // 1. 直接赋值:子类对象赋值给父类对象
        Dog dog = new Dog("小七", 1);
        
        eatFood(cat);
        eatFood(dog);
        
        Animal animal = buyAnimal("狗");
        animal.eat();
        
        animal = buyAnimal("猫");
        animal.eat();
    }
}

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

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

Ⅱ、向下转型

向下转型是指将一个父类类型的对象引用转换为一个子类类型的引用。这种转型通常在已经进行了向上转型后,需要重新获取原始子类类型的引用时使用。向下转型通常在以下场景中使用:

  • 已经进行了向上转型,需要再次使用原始子类的特定方法。
  • 在某些情况下,需要访问子类特有的属性或方法。

在这里插入图片描述

public class TestAnimal {
    public static void main(String[] args) {
        Cat cat = new Cat("元宝",2);
        Dog dog = new Dog("小七", 1);
        // 向上转型
        Animal animal = cat;
        animal.eat();
        animal = dog;
        animal.eat();
        // 编译失败,编译时编译器将animal当成Animal对象处理
        // 而Animal类中没有bark方法,因此编译失败
        // animal.bark();
        // 向上转型
        // 程序可以通过编程,但运行时抛出异常---因为:animal实际指向的是狗
        // 现在要强制还原为猫,无法正常还原,运行时抛出:ClassCastException
        cat = (Cat)animal;
        cat.mew();
        // animal本来指向的就是狗,因此将animal还原为狗也是安全的
        dog = (Dog)animal;
        dog.bark();
    }
}

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

public class TestAnimal {
    public static void main(String[] args) {
        Cat cat = new Cat("元宝",2);
        Dog dog = new Dog("小七", 1);
        // 向上转型
        Animal animal = cat;
        animal.eat();
        animal = dog;
        animal.eat();
        if(animal instanceof Cat){
            cat = (Cat)animal;
            cat.mew();
        }
        if(animal instanceof Dog){
            dog = (Dog)animal;
            dog.bark();
        }
    }
}

4、多态的优缺点

Ⅰ、多态的优点

多态最大的优点就是,能够降低代码的“圈复杂度”避免使用大量的 if - else

什么叫 “圈复杂度” ?

圈复杂度是一种描述一段代码复杂程度的方式。 一段代码如果平铺直叙, 那么就比较简单容易理解。而如果有很多的条件分支或者循环语句,就认为理解起来更复杂,因此我们可以简单粗暴的计算一段代码中条件语句和循环语句出现的个数, 这个个数就称为"圈复杂度".如果一个方法的圈复杂度太高, 就需要考虑重构,不同公司对于代码的圈复杂度的规范不一样,一般不会超过 10

例如我们现在需要打印多个形状,如果不基于多态实现代码如下:

public static void drawShapes() {
    Rect rect = new Rect();
    Cycle cycle = new Cycle();
    Flower flower = new Flower();
    String[] shapes = {"cycle", "rect", "cycle", "rect", "flower"};
    for (String shape : shapes) {
    	if (shape.equals("cycle")) {
    		cycle.draw();
    } else if (shape.equals("rect")) {
    	rect.draw();
    } else if (shape.equals("flower")) {
   		flower.draw();
    	}
    }
}

如果使用多态,则不必写这么多的 if - else 分支语句,代码更简单

public static void drawShapes() {
    // 我们创建了一个 Shape 对象的数组.
    Shape[] shapes = {new Cycle(), new Rect(), new Cycle(),
		new Rect(), new Flower()};
	for (Shape shape : shapes) {
		shape.draw();
	}
}

Ⅱ、多态的缺点

代码运行效率降低,多态性可能引入性能开销,因为在运行时需要进行动态绑定。相比于直接调用一个已知的方法,通过父类引用调用子类对象的方法可能会更加耗时。

属性没有多态性,构造方法没有多态性。当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性

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

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

相关文章

Linux加强篇004-Vim编辑器与Shell命令脚本

目录 前言 1. Vim文本编辑器 1.1 编写简单文档 1.2 配置主机名称 1.3 配置网卡信息 1.4 配置软件仓库 2. 编写Shell脚本 2.1 编写简单的脚本 2.2 接收用户的参数 2.3 判断用户的参数 3. 流程控制语句 3.1 if条件测试语句 3.2 for条件循环语句 3.3 while条件循环语…

spring aop核心原理概念

目录 概述aop核心概念解析Target(目标对象)Joinpoint(连接点)Advice(通知/增加)Pointcut(切入点)Aspect(切面)Advisor(通知器)Weaving(织入)Proxy(代理)Introduction(引介) 结束 概述 aop核心概念解析 Target(目标对象) 代理的目标对象 目标对象(Target)的确立,是…

C++ 实现位图

引出 面试题:给出 40 亿个不重复的无符号整数,没有排过序。给定一个无符号整数,如何快速判断这个数是否在这 40 亿个无符号整数中。[ 腾讯面试题 ] 想法一:将 40 亿个数据存放到 set 里面,然后再查找指定的无符号整数。…

深度学习技巧应用30-深度学习中的GPU的基本架构原理与应用技巧

大家好,我是微学AI,今天给大家介绍一下深度学习技巧应用30-深度学习中的GPU的基本架构原理与应用技巧,GPU是一种专门用于处理大量并行操作的硬件设备,它的架构设计主要是为了图形渲染。然而,由于其并行处理能力,现在广泛应用于深度学习、科学计算等领域。主要的GPU制造商…

运维高级--centos7源码安装Apache

安装必要的依赖项: sudo yum groupinstall "Development Tools" sudo yum install pcre pcre-devel zlib zlib-devel openssl openssl-devel这将安装编译和构建所需的基本工具,以及 Apache HTTP Server 所需的一些依赖项。 下载 Apache HTT…

从0到0.01入门 Webpack| 008.精选 Webpack面试题

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云…

【网络】DNS协议、ICMP协议、NAT技术

DNS协议、ICMP协议、NAT技术 一、DNS协议1、产生背景2、域名简介3、域名解析的工作流程4、使用dig工具分析DNS过程 二、ICMP协议1、ICMP介绍2、ICMP协议格式3、ping命令4、traceroute命令 三、NAT技术1、NAT技术背景2、NAT IP转换过程3、地址转换表4、NAPT技术5、重新理解路由器…

JsonRPC协议详解(协议介绍、请求示例、响应示例)

JsonRPC协议详解 什么是RPC? RPC(远程过程调用)是一种用于实现分布式系统中不同进程或不同计算机之间通信的技术。它允许我们像调用本地函数一样调用远程计算机上的函数,使得分布式系统的开发变得更加简单和高效。 什么是JsonRP…

【数据结构实验】图(三)图的深度优先搜索(DFS)生成树

文章目录 1. 引言2. 深度优先搜索生成树3. 实验内容3.1 实验题目(一)输入要求(二)输出要求 3.2 算法实现1. 数据结构2. 队列操作函数3. 广度优先搜索遍历4. 创建图5. 深度优先搜索算法6. 主函数及DFS主函数7. 输出生成树信息 3.3 …

第一百八十回 介绍两种阴影效果

文章目录 1. 概念介绍2. 实现方法3. 代码与效果3.1 示例代码3.2 运行效果 4. 内容总结 我们在上一章回中介绍了"自定义SlideImageSwitch组件"相关的内容,本章回中将介绍两种阴影效果.闲话休提,让我们一起Talk Flutter吧。 1. 概念介绍 我们在…

【服务器能干什么】二十分钟搭建一个属于自己的 RSS 服务

如果大家不想自己捣鼓,只是想尝尝鲜,可以在下面留言,我后台帮大家开几个账号玩一玩。 哔哩哔哩【高清版本可以点击去吐槽到 B 站观看】:【VPS服务器到底能干啥】信息爆炸的年代,如何甄别出优质的内容?你可能需要自建一个RSS服务!_哔哩哔哩_bilibili 前言 RSS 服务 市…

面试:双线程交替打印奇偶数

代码如下: package practice1;/*** 0-100的奇数偶数打印* 1、通过对象的wait和notify进行线程阻塞* 2、通过对num%2的结果进行奇数偶数的判断输出**/ public class JiOuOne {private static volatile int num 0;private static final int max 100;public static …

#define例题

我们已经学了#define的所有知识,让我们来看这道题,可不要又陷入陷阱 题目要求: #define N 4 #define Y(n) ((N2)*n) int main() {int z 2 * (N Y(5 1));printf("z%d\n", z);return 0; } 求这个z的值是多少? 我们直接…

Stable-Diffusion——Windows部署教程

Windows 参考文章:从零开始,手把手教你本地部署Stable Diffusion Webui AI绘画(非最新版) 一键脚本安装 默认环境安装在项目路径的venv下 conda create -n df_env python3.10安装pytorch:(正常用国内网就行) python -…

Sublime Text 3 安装离线插件 anaconda

1 下载 Sublime Text 3 免安装版 Download - Sublime Text 2 下载 Package Control,放到 Sublime Text Build 3211\Data\Installed Packages 目录下。 Installation - Package Control 3 页面搜索 anaconda anaconda - Search - Package Control Anaconda - Pac…

车载通信架构 —— 传统车内通信网络MOST总线(光纤传输、专精多媒体)

车载通信架构 —— 传统车内通信网络MOST总线(光纤传输、专精多媒体) 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都…

自建私有化证书颁发机构(Certificate Authority,CA)实战之 《0x02 Nginx 配置 https双向认证》

自建CA实战之 《0x02 Nginx 配置 https双向认证》 上一章节我们已经实现了Nginx上配置https单向认证,主要场景为客户端验证服务端的身份,但是服务端不验证客户端的身份。 本章节我们将实现Nginx上配置https双向认证,主要场景为客户端验证服…

Day41力扣打卡

打卡记录 第 N 位数字(找规律) 链接 class Solution:def findNthDigit(self, n: int) -> int:count, digit, start 9, 1, 1while n > count:n - countdigit 1start * 10count start * 9 * digitnum start (n - 1) // digitreturn int(str(n…

详解#define

我们要知道,#define后面定义的标识符只进行替换而不进行计算,我们不能根据惯性自动给它计算了,这样可能会出错。 目录 1.关于#define 1.1#define定义标识符 1.2#define定义宏 1.3#define的替换规则 2.#和## 1.# 2.## 3.带副作用的宏参…

1.4 8位加法器

1.半加器 2.全加器 半加器: 完整模拟1位加法 1.A,B 接受端,接受1或0 , 2个电信号 2.异或门 做为结果: 1^10, 0^00, 1^01, 0^11 与编程中的: 异或一致 3.与门 做为进位: 1&11,1&00,0&10, 0&01 与编程中的: 与一致 4.半加器实现1位的加法运算,比如:A端: …