一篇文章带你搞懂Java多态的概念、优点、实现多态的方式、以及不同方式的区别

一篇文章带你搞懂Java多态的概念、优点、使用场景

基本概念

​ **多态(Polymorphism)是面向对象编程的一个重要特性,它指的是同一个行为具有多个不同表现形式或形态的能力。**它允许我们使用父类的引用变量来引用子类的对象,并根据实际对象的类型调用相应的方法。多态就是同一个接口,使用不同的实例而执行不同操作,

如图所示:

img

多态性是对象多种表现形式的体现。

现实中,比如我们按下 F1 键这个动作:

  • 如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;
  • 如果当前在 Word 下弹出的就是 Word 帮助;
  • 在 Windows 下弹出的就是 Windows 帮助和支持。

同一个事件发生在不同的对象上会产生不同的结果。


上篇文章我们提到了方法的重写和重载

  • 重写体现的是父类和子类的多态(也就是动态多态也称运行时多态)
  • 重载体现的是一个类的多态(也就是静态多态也称编译时多态)

多态存在的三个必要条件

  1. 继承
  2. 重写
  3. 父类引用指向子类对象:Animal b = new Dog();

如下图:

img

class Shape {
    void draw() {} // 父类 Shape 中的绘制方法
}

class Circle extends Shape {
    void draw() { // 子类 Circle 中重写的绘制方法
        System.out.println("Circle.draw()");
    }
}

class Square extends Shape {
    void draw() { // 子类 Square 中重写的绘制方法
        System.out.println("Square.draw()");
    }
}

class Triangle extends Shape {
    void draw() { // 子类 Triangle 中重写的绘制方法
        System.out.println("Triangle.draw()");
    }
}

​ 基类 Shape 定义了一个名为 draw 的方法,这个方法在子类中可以被重写。子类 CircleSquareTriangle 分别继承自 Shape 类,并且在每个子类中都重写了 draw 方法以实现特定的绘制行为。

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。

多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。

以下是一个多态实例的演示,详细说明请看注释:

public class Test {
    public static void main(String[] args) {
      show(new Cat());  // 以 Cat 对象调用 show 方法
      show(new Dog());  // 以 Dog 对象调用 show 方法
                
      Animal a = new Cat();  // 向上转型  
      a.eat();               // 调用的是 Cat 的 eat,会动态绑定到Cat类的eat()
      Cat c = (Cat)a;        // 向下转型  
      c.work();        // 调用的是 Cat 的 work
  }  
            
    public static void show(Animal a)  {
      a.eat();  
        // 类型判断,在进行向下转型之前,最好使用 instanceof 运算符进行判断,以避免类型转换异常的发生。
        if (a instanceof Cat)  {  // 猫做的事情 
            Cat c = (Cat)a;  // 向下转型
            c.work();  
        } else if (a instanceof Dog) { // 狗做的事情 
            Dog c = (Dog)a;  // 向下转型
            c.work();  
        }  
    }  
}
 
abstract class Animal {  
    abstract void eat();  
}  
  
class Cat extends Animal {  
    public void eat() {  
        System.out.println("吃鱼");  
    }  
    public void work() {  
        System.out.println("抓老鼠");  
    }  
}  
  
class Dog extends Animal {  
    public void eat() {  
        System.out.println("吃骨头");  
    }  
    public void work() {  
        System.out.println("看家");  
    }  
}

执行以上程序,输出结果为:

吃鱼
抓老鼠
吃骨头
看家
吃鱼
抓老鼠

虚函数

虚函数的存在是为了多态。

**Java 中其实没有虚函数的概念,**它的普通函数就相当于 C++ 的虚函数,动态绑定是Java的默认行为。如果 Java 中不希望某个函数具有虚函数特性,可以加上 final 关键字变成非虚函数。


多态的实现方式

当我们谈论多态时,通常有4种主要的方式来实现它:

1.继承和方法重写(Inheritance and Method Overriding):
  • 子类可以继承父类的方法,并且可以对该方法进行重写。
  • 使用父类的引用变量来引用子类的对象,实现多态性。编译时类型为父类,运行时类型为子类。

示例:

class Animal {
    void makeSound() {
        System.out.println("动物发出声音");
    }
}

class Dog extends Animal {
    @Override
    void makeSound() {
        System.out.println("狗发出汪汪声");
    }
}

class Cat extends Animal {
    @Override
    void makeSound() {
        System.out.println("猫发出喵喵声");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal1 = new Dog();
        Animal animal2 = new Cat();

        animal1.makeSound(); // 输出 "狗发出汪汪声"
        animal2.makeSound(); // 输出 "猫发出喵喵声"
    }
}
2.接口实现(Interface Implementation):
  • 接口是一种将方法声明抽象化的机制,一个类可以实现一个或多个接口,并实现接口中定义的方法。
  • 使用接口类型的引用变量来引用实现该接口的类的对象,实现多态性。编译时类型为接口,运行时类型为实现类。

示例:

interface Drawable {
    void draw();
}

class Circle implements Drawable {
    @Override
    public void draw() {
        System.out.println("绘制圆形");
    }
}

class Square implements Drawable {
    @Override
    public void draw() {
        System.out.println("绘制正方形");
    }
}

public class Main {
    public static void main(String[] args) {
        Drawable shape1 = new Circle();
        Drawable shape2 = new Square();

        shape1.draw(); // 输出 "绘制圆形"
        shape2.draw(); // 输出 "绘制正方形"
    }
}
3.方法重载(Method Overloading):
  • 在同一个类中定义多个同名但参数列表不同的方法。
  • 根据传入的参数类型和数量来确定调用哪个重载方法,实现多态性。

示例:

class Calculator {
    int add(int a, int b) {
        return a + b;
    }

    double add(double a, double b) {
        return a + b;
    }
}

public class Main {
    public static void main(String[] args) {
        Calculator calculator = new Calculator();

        int result1 = calculator.add(2, 3); // 调用 int 版本的 add 方法
        double result2 = calculator.add(2.5, 3.7); // 调用 double 版本的 add 方法

        System.out.println(result1); // 输出 5
        System.out.println(result2); // 输出 6.2
    }
}
4.抽象类和抽象方法
  • 抽象类是一种不能被实例化的类,其中可以包含抽象方法,而抽象方法是只有声明而没有具体实现的方法。
  • 使用抽象类和抽象方法可以定义一个约束子类的模板,子类必须实现抽象类中声明的抽象方法。通过创建子类的对象,然后通过抽象类的引用来引用子类的对象,实现多态性。编译时类型为抽象类,运行时类型为子类。

示例:

abstract class Shape {
    abstract void draw();
}

class Circle extends Shape {
    @Override
    void draw() {
        System.out.println("绘制圆形");
    }
}

class Square extends Shape {
    @Override
    void draw() {
        System.out.println("绘制正方形");
    }
}

public class Main {
    public static void main(String[] args) {
        Shape shape1 = new Circle();
        Shape shape2 = new Square();

        shape1.draw(); // 输出 "绘制圆形"
        shape2.draw(); // 输出 "绘制正方形"
    }
}

在上面的例子中,Shape 类是一个抽象类,其中定义了一个抽象方法 draw()Circle 类和 Square 类都是 Shape 类的子类,并且必须实现 draw() 方法。通过抽象类 Shape 的引用变量分别引用 Circle 对象和 Square 对象,实现了多态性。

这四种方式都可以实现多态性,并且使代码更加灵活、可扩展,提高了可读性和可维护性。
PS:方法重载仅在同一个类中有效,不能实现跨类的多态性,而继承和方法重写、接口实现可以实现跨类的多态性。


多态的优点

  1. 消除类型之间的耦合关系
  2. 可替换性
  3. 可扩充性
  4. 接口性
  5. 灵活性
  6. 简化性

结尾语:记录于2023年8月2号11时16分,以上仅为个人在Java多态—菜鸟教程的学习过程中遇到的问题,还有记录的个人想法,有错误欢迎指出,希望对您有帮助,感谢观看!如果可以的话,点点赞,点点关注

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot第29讲:SpringBoot集成MySQL - MyBatis-Plus代码自动生成

SpringBoot第29讲:SpringBoot集成MySQL - MyBatis-Plus代码自动生成 本文是SpringBoot第29讲,主要介绍 MyBatis-Plus代码自动生成,以及产生此类代码生成工具的背景和此类工具的基本实现原理。 文章目录 SpringBoot第29讲:SpringBo…

STM32(HAL)串口中断接收

目录 1、简介 2 基础配置 2.1.1 SYS配置 2.1.2 RCC配置 2.2 串口外设配置 2.3 项目生成 3、KEIL端程序整合 1、简介 本文对HAL串口中断函数进行介绍。 2 基础配置 2.1.1 SYS配置 2.1.2 RCC配置 2.2 串口外设配置 2.3 项目生成 3、KEIL端程序整合 首先在main.c文件中进行…

一套AI+医疗模式的医院智慧导诊系统源码:springboot+redis+mybatis plus+mysql

一套AI医疗模式的医院智慧导诊系统源码 相关技术: 技术架构:springbootredismybatis plusmysqlRocketMQ 开发语言:java 开发工具:IDEA 前端框架:Uniapp 后端框架:springboot 数 据 库:mys…

华为OD机试真题 JavaScript 实现【名字的漂亮度】【牛客练习题】

目录 一、题目描述二、输入描述三、输出描述四、解题思路五、JavaScript算法源码 华为OD机试 2023B卷题库疯狂收录中,刷题点这里 刷的越多,抽中的概率越大,每一题都有详细的答题思路、详细的代码注释、样例测试,发现新题目&#x…

React的UmiJS搭建的项目集成海康威视h5player播放插件H5视频播放器开发包 V2.1.2

最近前端的一个项目,大屏需要摄像头播放,摄像头厂家是海康威视的,网上找了一圈都没有React集成的,特别是没有使用UmiJS搭脚手架搭建的,所以记录一下。 海康威视的开放平台的API地址,相关插件和文档都可以下…

CUDA_CHECK(cudaFree(...))报错CUDA error 1

GPT-3.5太好用了,报错情况如下: 总结一下, 在使用cudaFree释放之前cudaMalloc()分配的GPU内存时,报错cuda error,最有可能的几个原因就是: 试图释放已经释放的gpu内存,在调用cudafree()时确保没…

C# Solidworks二次开发:自动创建虚拟零件及使用注意事项

今天要讲的是关于在solidworks中如何自动创建虚拟零件的功能,也就是solidworks中插入新零件这个功能。 实现这个功能需要使用的API如下所示: InsertNewVirtualPart(swFaceOrPlane1, out swcomp2); 其中这个方法中使…

【深度学习】InST,Inversion-Based Style Transfer with Diffusion Models,论文,风格迁移,实战

代码:https://github.com/zyxElsa/InST 论文:https://arxiv.org/abs/2211.13203 文章目录 AbstractIntroductionRelated WorkImage style transferText-to-image synthesisInversion of diffusion models MethodOverview ExperimentsComparison with Sty…

登月再进一步:Apollo自动驾驶的里程碑

前言 「作者主页」:雪碧有白泡泡 「个人网站」:雪碧的个人网站 「推荐专栏」: ★java一站式服务 ★ ★前端炫酷代码分享 ★ ★ uniapp-从构建到提升★ ★ 从0到英雄,vue成神之路★ ★ 解决算法,一个专栏就够了★ ★ 架…

spark-sql数据重复之File Output Committer问题

前言 我们先来回顾下之前介绍过的三种Committer:FileOutputCommitter V1、FileOutputCommitter V2、S3A Committer,其基本代表了整体的演进趋势。 核心代码讲解详细参照:Spark CommitCoordinator 保证数据一致性 OutputCommitter commitTask…

Android如何用系统签名打包应用

前言 应用使用系统签名可以在用户不需要手动授权的情况下自动获取权限。适合一些定制系统中集成apk的方案商。 步骤 需要在AndroidManifest.xml中添加共享系统进程属性: android:sharedUserId"android.uid.system"如下图所示: 找到系统定制…

C++函数模板

2023年8月2日&#xff0c;周三上午 目录 模板的分类语法模板实例化示例程序深入理解模板参数 模板的分类 模板分为函数模板和类模板。 语法 template<typename 模板参数1,...> 返回值类型 函数名(参数列表){} template是一个关键字&#xff0c;用于声明模板 <>…

MPLS虚拟专用网跨域--OptionB方案

OptionB方案 跨域VPN-OptionB中,两个ASBR通过MP-EBGP交换它们从各自AS的PE设备接收的标签VPN-IPv4路由。图中,VPN LSP表示私网隧道,LSP表示公网隧道。 跨域VPN-OptionB方案中,ASBR接收本域内和域外传过来的所有跨域VPN-IPv4路由,再把VPN-IPv4路由发布出去。但MPLS VPN的…

C++多线程环境下的单例类对象创建

使用C无锁编程实现多线程下的单例模式 贺志国 2023.8.1 一、尺寸较小的类单例对象创建 如果待创建的单例类SingletonForMultithread内包含的成员变量较少&#xff0c;整个类占用的内存空间较小&#xff0c;则可以使用如下方法来创建单例对象&#xff08;如果类的尺寸较大则不…

穷举深搜暴搜回溯剪枝(1)

一)全排列: 46. 全排列 - 力扣&#xff08;LeetCode&#xff09; 1)先画出决策树: 越详细越好&#xff0c;就是我们在进行暴力枚举这道题的过程中&#xff0c;如何不重不漏地将所有的情况全部枚举到&#xff0c;把这个思想历程给画下来&#xff0c;就可以了&#xff0c;把每一步…

PHP高级检索功能的实现以及动态拼接sql

我们学习了解了这么多关于PHP的知识&#xff0c;不知道你们对PHP高级检索功能的实现以及动态拼接sql是否已经完全掌握了呢&#xff0c;如果没有&#xff0c;那就跟随本篇文章一起继续学习吧! PHP高级检索功能的实现以及动态拼接sql。完成的功能有&#xff1a;可以单独根据一个…

k8s部署xxl-job分布式任务调度服务

一、背景 什么时候需要把xxl-job部署到k8s里 当你的java服务部署到K8S后&#xff0c;因为xxl-job的任务调度器需要对注册上来的执行器进行健康检测&#xff0c;而java服务作为执行器&#xff0c;注册地址是pod的Ip地址&#xff1b;所以&#xff0c;调度器想要访问执行器的网路…

DevExpress WPF Tree List组件,让数据可视化程度更高!(二)

DevExpress WPF Tree List组件是一个功能齐全、数据感知的TreeView-ListView混合体&#xff0c;可以把数据信息显示为REE、GRID或两者的组合&#xff0c;在数据绑定或非绑定模式下&#xff0c;具有完整的数据编辑支持。 在上文中&#xff08;点击这里回顾DevExpress WPF Tree …

JavaScript 简单实现观察者模式和发布-订阅模式

JavaScript 简单实现观察者模式和发布-订阅模式 1. 观察者模式1.1 什么是观察者模式1.2 代码实现 2. 发布-订阅模式2.1 什么是发布-订阅模式2.2 代码实现2.2.1 基础版2.2.2 取消订阅2.2.3 订阅一次 1. 观察者模式 1.1 什么是观察者模式 概念&#xff1a;观察者模式定义对象间…

IBM Spectrum LSF (“LSF“ ,简称为负载共享设施) 用户案例

IBM Spectrum LSF (“LSF” &#xff0c;简称为负载共享设施) 用户案例 IBM Spectrum LSF (“LSF” &#xff0c;简称为负载共享设施) 软件是业界领先的企业级软件。 LSF 在现有异构 IT 资源之间分配工作&#xff0c;以创建共享&#xff0c;可扩展且容错的基础架构&#xff0c…