探索 Java 多态的奥秘

embedded/2025/2/11 15:47:17/

一、引言

在面向对象编程中,多态是核心概念之一,它允许我们用一个接口或父类的引用操作多个不同子类对象,从而实现灵活的代码复用和扩展。本篇博客将借助一组具体的 Java 代码示例,深入剖析多态的实现机制,并探讨其在实际开发中的应用。

二、多态概述

多态有三种主要表现形式:

  • 编译时多态:主要指方法的重载(Overloading),通过方法名相同、参数列表不同实现。

  • 运行时多态:主要指方法的重写(Overriding),通过子类重写父类方法实现,运行时根据对象的实际类型动态绑定方法。

在 Java 中,运行时多态的应用更为广泛,它借助 向上转型动态绑定 机制,使得程序具有更高的灵活性和扩展性。

三、代码示例分析:多态的核心机制

1. 示例代码

以下是本篇博客的核心代码,我们将逐行分析其多态特性:

// 文件 1:Test.java
package com;public class Test {public static void main(String[] args) {A a1 = new A();          // 父类对象A a2 = new B();          // 向上转型B b1 = new B();          // 子类对象C c = new C();           // 子类对象D d = new D();           // 子类对象System.out.println(a1.Show());           System.out.println(a2.Show());           System.out.println(b1.Show());           // 方法重载示例System.out.println(a1.Show(b1));         System.out.println(a1.Show(c));          System.out.println(a1.Show(d));          System.out.println(a2.Show(b1));         System.out.println(a2.Show(c));          System.out.println(a2.Show(d));          System.out.println(b1.Show(b1));         System.out.println(b1.Show(c));          System.out.println(b1.Show(d));         }
}
2. 类层次结构

本示例涉及以下类及继承关系:

  • A 类:父类,包含 Show() 方法和多个重载的 Show() 方法。

  • B 类:继承自 A,重写了部分 Show() 方法并添加了新的重载方法。

  • C 类:继承自 B,未重写方法。

  • D 类:继承自 B,未重写方法。

类定义如下:

// 文件 3:A.java
public class A {public String Show(D obj) {return "A and D";}public String Show(A obj) {return "A and A";}public String Show() {return "无参的A";}
}// 文件 4:B.java
public class B extends A {public String Show(Object obj) {return "B and B";}public String Show(A obj) {return "B and A";}public String Show() {return "无参的B";}
}// 文件 5:C.java
public class C extends B {}// 文件 6:D.java
public class D extends B {}
3. 多态的实现
  • 向上转型:如 A a2 = new B();,将子类 B 的对象赋值给父类 A 类型的引用变量,此时 a2 的编译时类型为 A,运行时类型为 B

  • 动态绑定:当调用 a2.Show() 时,Java 会根据 a2 的运行时类型(B)来决定调用 B 类的重写方法,而不是 A 类的方法。

四、运行结果与分析

1. 调用 a1.Show()a2.Show()
// 调用方法:a1.Show()
System.out.println(a1.Show());   // 输出:无参的A// 调用方法:a2.Show()
System.out.println(a2.Show());   // 输出:无参的B
  • 分析

    • a1A 类的对象,编译时和运行时类型均为 A,所以调用 A 类的 Show() 方法。

    • a2B 类的对象,尽管编译时类型为 A,但运行时类型为 B,因此调用 B 类重写的 Show() 方法。

2. 方法重载解析
System.out.println(a1.Show(b1));   // 输出:A and B
  • 编译时解析

    • 在编译阶段,Java 根据参数的 编译时类型 和方法签名选择方法。b1 的编译时类型为 B

    • A 类中没有参数为 BShow 方法,因此向上转型到 A 的父类吗?

注意

  • A 类的 Show(A obj) 方法存在,而 BA 的子类,Show(A obj) 是更匹配的参数类型吗?因此,a1.Show(b1) 会调用 A 类的 Show(A obj) 方法,返回 "A and A"

b1 的类型是 BB 继承自 A,因此 BA 的子类。因此,当调用 a1.Show(b1) 时,b1编译时类型B,Java 会查找 A 类中是否有参数类型为 B 的方法。如果没有,就会向上转型到 B 的父类,即 A,所以找到 Show(A obj) 方法。

B 类型的对象作为参数传递,因为 BA 的子类,所以 Show(A obj) 是最匹配的。

在方法重载解析时,参数的编译时类型决定了方法的选择。BA 的子类,因此 Show(A obj) 是匹配的,因此调用该方法,返回 "A and A"

3. 子类方法重写与调用
System.out.println(a2.Show());   // 输出:无参的B
  • 分析

    • a2B 类的对象,但其编译时类型为 A

    • 当调用 a2.Show() 时,Java 首先在 A 类及其子类中查找方法。

    • 由于 B 类重写了 Show() 方法,运行时绑定到 B 类的实现,因此输出 B 类的无参 Show 方法返回值。

4. 代码逐个分析与内存图展示
 System.out.println(a1.Show(c));   //输出:A and ASystem.out.println(a1.Show(d));   //输出:A and D   
  • 分析

    • a1是A类对象,运行时和编译时都为A,只能调用A中的三个方法,里边传参为C类对象c,而能传参的两个方法没有传C类的,所以C向上转型为B类,发现也没有传参B类的,于是再次向上转型为A类,调用show(A obj)方法

    • 第二个和第一个区别为不需要向上转型,因为d是D类对象,而A中的方法本来就有传D类对象的,因此直接调用show(D obj)方法即可

System.out.println(a2.Show(b1));   //输出:B and A      
System.out.println(a2.Show(c));    //输出:B and A      
System.out.println(a2.Show(d));    //输出:A and D
  • 分析

    • 如图a2只能调用A中的show(D obj),B中的show(A obj)和B中的show()方法,传参为B类型向上转型为A类所以调用B中的show(A obj)方法

    • 如图a2只能调用A中的show(D obj),B中的show(A obj)和B中的show()方法,传参为C类型向上转型为B类再向上转型为A类所以调用B中的show(A obj)方法

    • 如图a2只能调用A中的show(D obj),B中的show(A obj)和B中的show()方法,传参为D类型向上转型为A类所以调用A中的show(D obj)方法

System.out.println(b1.Show(b1));   //输出: B and A     
System.out.println(b1.Show(c));    //输出: B and A    
System.out.println(b1.Show(d))     //输出: A and D
  • 分析

    • 如图b1可以调用B中的所有方法和A中的show(D obj)方法,传参为b1时为B类型,向上转型为A类,调用B中的show(A obj)方法

    • 如图b1可以调用B中的所有方法和A中的show(D obj)方法,传参为c时为C类型,向上转型为A类,调用B中的show(A obj)方法

    • 如图b1可以调用B中的所有方法和A中的show(D obj)方法,传参为d时为D类型直接调用A中的show(D obj)方法

五、多态的实际应用

多态在以下场景中发挥着巨大作用:

  1. 代码扩展性:通过父类引用管理子类对象,无需修改现有代码即可添加新功能。

  2. 接口编程:基于接口或抽象类进行编程,提高代码的灵活性和可维护性。

  3. 框架设计:如 Spring 框架大量使用多态机制,通过依赖注入实现组件的灵活装配。

六、总结

通过本文的详细分析,我们了解到 Java 多态的实现机制和应用价值。多态使得程序更加灵活、可扩展,是面向对象编程的核心优势之一。希望读者通过本文能够深刻理解多态的原理,并在实际开发中熟练运用。

七、完整代码

以下是完整的代码示例:

// Test.java
package com;public class Test {public static void main(String[] args) {A a1 = new A();A a2 = new B();B b1 = new B();C c = new C();D d = new D();System.out.println(a1.Show());System.out.println(a2.Show());System.out.println(b1.Show());System.out.println(c.Show());System.out.println(d.Show());System.out.println(a1.Show(b1));System.out.println(a1.Show(c));System.out.println(a1.Show(d));System.out.println(a2.Show(b1));System.out.println(a2.Show(c));System.out.println(a2.Show(d));System.out.println(b1.Show(b1));System.out.println(b1.Show(c));System.out.println(b1.Show(d));}
}// A.java
package com;public class A {public String Show(D obj) {return "A and D";}public String Show(A obj) {return "A and A";}public String Show() {return "无参的A";}
}// B.java
package com;public class B extends A {public String Show(Object obj) {return "B and B";}public String Show(A obj) {return "B and A";}public String Show() {return "无参的B";}
}// C.java
package com;public class C extends B {}// D.java
package com;public class D extends B {}

八、知识点回顾

  • 向上转型:将子类对象赋值给父类引用,编译时类型为父类,运行时类型为子类。

  • 动态绑定:运行时根据对象的实际类型调用方法,实现多态。

  • 方法重载:通过参数列表不同实现多态,依赖编译时解析。

  • 方法重写:通过子类重写父类方法实现多态,依赖运行时绑定。


http://www.ppmy.cn/embedded/161361.html

相关文章

【jmeter】在windows中,创建的变量,在jmeter中,读取变量失败的问题,路径问题

1.0 在windows中,jmeter读取变量失败 在路径配置的时候,配置按照D:\FtpDownload\${file_name}运行之后,下载的文件,文件名出现问题 \取消了$符号的意义,所以需要更改路径 D:\\FtpDownload\\${file_name}

stm32小白成长为高手的学习步骤和方法

我们假定大家已经对STM32的书籍或者文档有一定的理解。如不理解,请立即阅读STM32的文档,以获取最基本的知识点。STM32单片机自学教程 这篇博文也是一篇不错的入门教程,初学者可以看看,讲的真心不错。 英文好的同学&#xf…

基于 gitee 的 CI/CD

基于 gitee 的 CI/CD 流程简介。 CI/CD 流程是指在软件开发过程中,通过自动化的方式实现代码的持续集成、持续部署和持续交付。 CI/CD 流程通常包括以下几个步骤: 代码提交:开发者将代码提交到代码仓库,如 Git、SVN 等。代码构建…

linux_kernel驱动开发_驱动调试

调试思路 对比开发板检查差异性。用试波器或万用表调试。配合硬件工程师共同解决问题。 开发思路 先在config中打开相应宏开关。设备树中加入相应配置即可。(可以扩展讲解宏开关有无生效) pcie 先lspci查看是否注册成功。然后才能看到其pcie设备 。…

Spring容器初始化扩展点:ApplicationContextInitializer

目录 一、什么是ApplicationContextInitializer?1、核心作用2、适用场景 二、ApplicationContextInitializer的使用方式1、实现ApplicationContextInitializer接口2、注册初始化器 三、ApplicationContextInitializer的执行时机四、实际应用案例1、动态设置环境变量…

springtask基本使用

Spring Task 介绍与基本使用 1. 什么是 Spring Task? Spring Task 是 Spring 框架提供的 轻量级任务调度模块,用于在应用程序中实现定时任务、周期性任务或延迟任务。它通过注解和配置简化了任务调度的开发,适用于单机环境下的简单调度需求。…

远程 IO 模块:汽车零部件产线的高效生产引擎

在汽车零部件生产的激烈竞争中,效率与质量是企业立足的根本。传统生产模式在面对日益增长的市场需求时,逐渐显露出短板。而明达技术MR20远程 IO 模块的出现,如同一束强光,照亮了汽车零部件生产高效发展的新道路。 MR20远程 IO 模块…

【Kubernetes的SpringCloud最佳实践】有Service是否还需要Eureka?

在 Kubernetes 中部署 Spring Cloud 微服务时,是否还需要 Eureka 取决于具体场景和架构设计。以下是详细的实践建议和结论: 1. Kubernetes 原生服务发现 vs Eureka Kubernetes 自身提供了完善的服务发现机制(通过 Service 资源)&…