[Java]多态
1. 多态的基本概念
1.1 定义:
多态是指同一操作作用于不同的对象时,能够表现出不同的行为。多态通常通过以下两种方式实现:
- 方法重载(Overloading)
- 方法重写(Overriding)
1.2 示例:
假设有一个父类Animal,它有一个sound方法。Dog和Cat类继承了Animal,并重写了sound方法。通过多态,可以让我们用统一的方式调用sound方法,但不同的对象会有不同的行为。
class Animal {void sound() {System.out.println("Animal makes a sound");}
}class Dog extends Animal {@Overridevoid sound() {System.out.println("Dog barks");}
}class Cat extends Animal {@Overridevoid sound() {System.out.println("Cat meows");}
}
2. 多态的两种实现方式
Java中的多态有两种主要形式:方法重载和方法重写。
2.1 方法重载(Overloading)方法重载的详细知识点可以看这篇笔记
方法重载是指在同一个类中,方法名相同,但参数列表不同(参数的类型、个数或者顺序不同)。方法的返回类型可以相同或不同。重载是静态多态,发生在编译时。
class Printer {void print(String message) {System.out.println(message);}void print(int number) {System.out.println(number);}
}
在上面的例子中,print方法被重载了,可以接收String类型或int类型的参数。
2.2 方法重写(Overriding)方法重写的详细知识点可以看这篇笔记
方法重写是指子类重新实现父类的某个方法,方法名、参数列表和返回类型必须和父类中的方法完全相同。重写是动态多态,发生在运行时。
class Animal {void sound() {System.out.println("Animal makes a sound");}
}class Dog extends Animal {@Overridevoid sound() {System.out.println("Dog barks");}
}class Cat extends Animal {@Overridevoid sound() {System.out.println("Cat meows");}
}
在这个例子中,Dog和Cat类重写了Animal类中的sound方法。通过多态,父类引用可以指向子类对象,并且调用子类的sound方法。
3. 多态的实现机制
3.1 Java中的多态主要通过以下方式实现:
- 父类引用指向子类对象:多态依赖于父类类型的引用指向子类对象。
- 方法的动态绑定:当调用父类引用的方法时,Java虚拟机会在运行时根据对象的实际类型来决定调用哪个方法。
3.2 示例:父类引用指向子类对象
public class Main {//通过一个方法实现不同的功能(Dog和Cat中有同一种sound方法,但是却能实现不同功能,这就叫多态)public static void main(String[] args) {Animal animal1 = new Dog(); // 父类引用指向子类对象animal1.sound(); // 输出:Dog barksAnimal animal2 = new Cat();animal2.sound(); // 输出:Cat meows}
}
4. 多态的好处
多态提供了许多编程上的好处:
- 代码复用:同一方法名可以用于不同的对象,减少了重复代码的编写。
- 提高灵活性和可扩展性:可以通过继承和实现接口来扩展程序,无需修改现有代码。
- 增强系统的可维护性:通过多态,父类和子类的关系更加清晰,修改父类的代码时,子类可以自动适应新的行为。
5. 运行时多态与编译时多态
- 运行时多态(动态绑定):通过方法重写实现,调用方法时,Java会根据对象的实际类型来选择方法。这是通过父类引用指向子类对象的方式来实现的。
- 编译时多态(静态绑定):通过方法重载实现,编译器会根据参数的类型来选择调用哪个方法。
5.1 运行时多态示例:(方法重写)
class Animal {void sound() {System.out.println("Animal makes a sound");}
}class Dog extends Animal {@Overridevoid sound() {System.out.println("Dog barks");}
}public class Main {public static void main(String[] args) {Animal animal = new Dog(); // 父类引用指向子类对象animal.sound(); // 输出:Dog barks}
}
5.2 编译时多态示例:(方法重载)
class Printer {void print(String message) {System.out.println(message);}void print(int number) {System.out.println(number);}
}public class Main {public static void main(String[] args) {Printer printer = new Printer();printer.print("Hello, World!"); // 调用print(String)printer.print(100); // 调用print(int)}
}
6. 向上转型与向下转型
6.1 向上转型(Upcasting):
向上转型是指将子类对象赋给父类变量,即将一个子类对象转型为父类类型。向上转型通常是安全的,因为子类对象本身是父类对象的一种特殊形式。向上转型不会丢失任何信息,因为父类包含了子类的所有公共方法和属性,父类引用可以指向子类的对象。
示例:
class Animal {void sound() {System.out.println("Animal makes a sound");}
}class Dog extends Animal {void sound() {System.out.println("Dog barks");}
}public class Main {public static void main(String[] args) {Dog dog = new Dog();Animal animal = dog; // 向上转型:Dog -> Animalanimal.sound(); // 输出:Dog barks}
}
解释:
dog是Dog类型的对象。- 通过
Animal animal = dog;进行向上转型,animal变成了Animal类型的引用,指向了Dog类型的对象。 - 即使
animal的类型是Animal,它依然会调用Dog类的sound()方法,这是因为 Java 支持动态方法调度(多态)。
6.2 向下转型(Downcasting)
向下转型是指将父类变量转换为子类类型。向下转型在运行时可能会引发 ClassCastException,因为父类引用的对象可能并不是子类的实例。因此,在执行向下转型之前,通常需要进行类型检查。
示例:
class Animal {void sound() {System.out.println("Animal makes a sound");}
}class Dog extends Animal {void sound() {System.out.println("Dog barks");}
}public class Main {public static void main(String[] args) {Animal animal = new Dog(); // 向上转型Dog dog = (Dog) animal; // 向下转型dog.sound(); // 输出:Dog barks}
}
解释:
animal是Animal类型的引用,它实际上指向一个Dog类型的对象。Dog dog = (Dog) animal;将animal引用向下转型为Dog类型,这时可以调用Dog类的方法。- 向下转型时需要确保对象的实际类型是目标类型,否则会抛出
ClassCastException。
类型检查
为了避免错误的向下转型,可以使用 instanceof 运算符来检查对象的类型。
if (animal instanceof Dog) {Dog dog = (Dog) animal; // 安全的向下转型dog.sound();
} else {System.out.println("animal is not a Dog");
}
7. 接口与多态
接口的详细知识点可以看这篇笔记
接口也支持多态。通过接口类型引用实现多态,不同的类实现同一个接口时,可以在运行时根据实际对象的类型调用不同的实现。
interface Animal {void sound();
}class Dog implements Animal {@Overridepublic void sound() {System.out.println("Dog barks");}
}class Cat implements Animal {@Overridepublic void sound() {System.out.println("Cat meows");}
}public class Main {public static void main(String[] args) {Animal animal1 = new Dog();animal1.sound(); // 输出:Dog barksAnimal animal2 = new Cat();animal2.sound(); // 输出:Cat meows}
}
8. 方法重载与多态的关系
方法重载和多态有区别,方法重载发生在编译时,而多态主要通过方法重写发生在运行时。方法重载是静态绑定,而多态是动态绑定。两者常常一起使用,但它们并不相同。
class Printer {void print(String s) {System.out.println("Printing string: " + s);}void print(int i) {System.out.println("Printing integer: " + i);}
}public class Main {public static void main(String[] args) {Printer printer = new Printer();printer.print("Hello");printer.print(123);}
}
9. 多态的注意事项
- 多态只有在继承或实现接口的情况下才会生效。
- 子类方法必须具有相同的签名(方法名、参数、返回类型)才能重写父类方法,保证多态的正确实现。
- 使用多态时,要注意类型转换,避免出现
ClassCastException。
10. 多态与构造方法的关系
构造方法不会被继承,且构造方法不能被重写。因此,在子类中调用父类的构造方法时,即使我们有多态的存在,父类构造方法的选择是静态的,和多态无关。
10.1 构造方法 不会被继承
在 Java 中,构造方法是类的特殊方法,用于创建对象并初始化它的状态。虽然子类可以调用父类的构造方法(使用 super()),但是构造方法本身不会被继承。也就是说,子类不能直接访问父类的构造方法,它只能通过 super() 显式地调用父类的构造方法。如果父类没有显式定义构造方法,Java 会自动提供一个默认的无参构造方法。
示例:
class Animal {Animal() {System.out.println("Animal constructor");}
}class Dog extends Animal {// Dog类没有继承Animal的构造方法,它必须显式调用父类的构造方法Dog() {super(); // 显式调用父类构造方法System.out.println("Dog constructor");}
}
解释:
Dog类并没有继承Animal类的构造方法,而是必须在自己的构造方法中显式地调用父类的构造方法(通过super())。- 即使
Dog是Animal的子类,它并没有自动继承Animal的构造方法,因此Dog类需要自己定义构造方法。
10.2 构造方法 不能被重写
构造方法不能被重写是指,子类无法用相同的签名来重写父类的构造方法。构造方法的目的是创建类的实例,因此它是与类的实例化过程紧密相关的。构造方法的名字必须和类的名字相同,而且每个类只能有一个构造方法。虽然子类可以定义一个构造方法,并在其中调用父类的构造方法,但它无法“重写”父类的构造方法。
示例:
class Animal {Animal() {System.out.println("Animal constructor");}
}class Dog extends Animal {// 这里并不是重写父类的构造方法,而是定义了自己的构造方法Dog() {super(); // 调用父类构造方法System.out.println("Dog constructor");}
}
解释:
- 在
Dog类中,虽然定义了一个构造方法,它并没有重写父类Animal的构造方法。Dog的构造方法是 新的 构造方法,而不是父类构造方法的重写。 - 构造方法无法被重写的原因在于,构造方法的调用是由实例化对象时自动进行的,而不能像普通方法一样通过多态进行动态绑定。
11. 多态与泛型
泛型是Java中用于实现类型安全的一种机制,通常和多态一起使用。通过泛型,你可以实现类型参数化的多态,使得代码在不丧失类型安全的情况下能够处理多种类型。
示例:
class Box<T> {private T value;public void setValue(T value) {this.value = value;}public T getValue() {return value;}
}public class Main {public static void main(String[] args) {Box<Integer> intBox = new Box<>();intBox.setValue(10);System.out.println(intBox.getValue()); // 输出:10Box<String> strBox = new Box<>();strBox.setValue("Hello");System.out.println(strBox.getValue()); // 输出:Hello}
}
这里的Box<T>类是泛型类,T代表一个类型参数。你可以通过不同的类型来创建Box对象,并通过多态灵活地处理这些不同的类型。
12. 多态与反射
反射是Java提供的一种强大的工具,它允许在运行时动态地访问类的方法、字段、构造方法等。反射和多态结合使用时,可以让我们动态地创建对象、调用方法,而不需要事先知道对象的类型。
示例:
import java.lang.reflect.Method;class Animal {public void sound() {System.out.println("Animal makes a sound");}
}class Dog extends Animal {@Overridepublic void sound() {System.out.println("Dog barks");}
}public class Main {public static void main(String[] args) throws Exception {Class<?> clazz = Class.forName("Dog");Animal animal = (Animal) clazz.getDeclaredConstructor().newInstance();// 使用反射动态调用方法Method method = clazz.getMethod("sound");method.invoke(animal); // 输出:Dog barks}
}
这里,Class.forName("Dog")动态加载了Dog类,Method.invoke()动态调用了Dog类的sound()方法,这种方式与多态结合非常有用。
13. 多态与抽象类的结合
抽象类的详细知识点可以看这篇笔记
抽象类可以包含具体的方法实现,也可以包含抽象方法供子类实现。在多态中,抽象类常常作为父类提供统一的行为规范,子类提供具体实现。抽象类和多态结合使得代码更加灵活和可扩展。
示例:
abstract class Animal {abstract void sound();
}class Dog extends Animal {@Overridevoid sound() {System.out.println("Dog barks");}
}class Cat extends Animal {@Overridevoid sound() {System.out.println("Cat meows");}
}public class Main {public static void main(String[] args) {Animal animal1 = new Dog();animal1.sound(); // 输出:Dog barksAnimal animal2 = new Cat();animal2.sound(); // 输出:Cat meows}
}
14. 多态与设计模式的关系
多态在很多设计模式中发挥着重要作用,尤其是在策略模式、工厂模式、模板方法模式等中。多态使得不同的实现可以被统一处理,从而使得系统更加灵活和可扩展。
- 策略模式:通过多态切换不同的策略。
- 工厂模式:使用工厂方法根据不同的条件返回不同类型的对象,通过多态来处理这些对象。
- 模板方法模式:通过多态允许子类重写父类的部分方法,从而使得流程控制更加灵活。
15. 虚拟机和多态
在Java的执行过程中,虚拟机会根据实际对象的类型来决定调用哪个方法,这个过程叫做动态方法分派(dynamic dispatch)。在多态中,虚拟机会根据对象的实际类型而不是引用类型来决定调用哪个版本的方法,这样就能实现运行时的多态性。
16. 接口默认方法与多态
从Java 8开始,接口可以包含默认方法(default)。这些方法可以在接口中提供具体实现,子类可以选择使用默认实现或者重写它。使用接口的默认方法可以实现一种多态机制,使得接口可以演变而不破坏现有代码。
示例:
interface Animal {default void sound() {System.out.println("Animal makes a sound");}
}class Dog implements Animal {@Overridepublic void sound() {System.out.println("Dog barks");}
}class Cat implements Animal {// 不重写sound方法,使用默认实现
}public class Main {public static void main(String[] args) {Animal dog = new Dog();dog.sound(); // 输出:Dog barksAnimal cat = new Cat();cat.sound(); // 输出:Animal makes a sound}
}
17. 多态与集合框架
在Java集合框架中,接口和多态结合非常紧密。例如,List、Set、Map等接口允许不同的实现类(如ArrayList、HashSet等)被统一处理,通过多态在运行时决定使用哪一种实现。
示例:
import java.util.List;
import java.util.ArrayList;public class Main {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("Apple");list.add("Banana");for (String fruit : list) {System.out.println(fruit);}}
}
在这个例子中,List接口通过多态允许不同类型的列表实现,如ArrayList、LinkedList等,它们都可以在运行时根据需要被使用。
相关文章:
[Java]多态
1. 多态的基本概念 1.1 定义: 多态是指同一操作作用于不同的对象时,能够表现出不同的行为。多态通常通过以下两种方式实现: 方法重载(Overloading)方法重写(Overriding) 1.2 示例࿱…...
用Impala对存储在HDFS中的大规模数据集进行快速、实时的交互式SQL查询的具体步骤和关键代码
AWS EMR(Elastic MapReduce)中应用Impala的典型案例,主要体现在大型企业和数据密集型组织如何利用Impala对存储在Hadoop分布式文件系统(HDFS)中的大规模数据集进行快速、实时的交互式SQL查询。以下是一个具体的案例说明…...
Intellij 插件开发-快速开始
目录 一、开发环境搭建以及创建action1. 安装 Plugin DevKit 插件2. 新建idea插件项目3. 创建 Action4. 向新的 Action 表单注册 Action5. Enabling Internal Mode 二、插件实战开发[不推荐]UI Designer 基础JBPanel类(JPanel面板)需求:插件设…...
GIt使用笔记大全
Git 使用笔记大全 1. 安装 Git 在终端或命令提示符中,输入以下命令检查是否已安装 Git: git --version如果未安装,可以从 Git 官方网站 下载并安装适合你操作系统的版本。 2. 配置 Git 首次使用 Git 时,需要配置用户名和邮箱…...
语言月赛 202412【题目名没活了】题解(AC)
》》》点我查看「视频」详解》》》 [语言月赛 202412] 题目名没活了 题目描述 在 XCPC 竞赛里,会有若干道题目,一支队伍可以对每道题目提交若干次。我们称一支队伍对一道题目的一次提交是有效的,当且仅当: 在本次提交以前&…...
MySQL锁类型(详解)
锁的分类图,如下: 锁操作类型划分 读锁 : 也称为共享锁 、英文用S表示。针对同一份数据,多个事务的读操作可以同时进行而不会互相影响,相互不阻塞的。 写锁 : 也称为排他锁 、英文用X表示。当前写操作没有完成前,它会…...
面经--C语言——static,volatile,malloc,使用异或进行数据交换
文章目录 static静态变量和全局变量的区别volatile主要作用 malloc1. 内存分配器的作用2. 内存分配过程(1) 查找空闲内存块(2) 扩展堆空间(3) 元数据 3. 内存释放过程(1) 标记为可用(2) 合并相邻空闲块(3) 延迟释放 4. 内存管理策略(1) 分配缓存(Allocation Caching…...
stm32小白成长为高手的学习步骤和方法
我们假定大家已经对STM32的书籍或者文档有一定的理解。如不理解,请立即阅读STM32的文档,以获取最基本的知识点。STM32单片机自学教程 这篇博文也是一篇不错的入门教程,初学者可以看看,讲的真心不错。 英文好的同学…...
OSCP - Proving Grounds - Roquefort
主要知识点 githook 注入Linux path覆盖 具体步骤 依旧是nmap扫描开始,3000端口不是很熟悉,先看一下 Nmap scan report for 192.168.54.67 Host is up (0.00083s latency). Not shown: 65530 filtered tcp ports (no-response) PORT STATE SERV…...
集合通讯概览
(1)通信的算法 是根据通讯的链路组成的 (2)因为通信链路 跟硬件强相关,所以每个CCL的库都不一样 芯片与芯片、不同U之间是怎么通信的!!!!!! 很重要…...
【贪心算法篇】:“贪心”之旅--算法练习题中的智慧与策略(二)
✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨ ✨ 个人主页:余辉zmh–CSDN博客 ✨ 文章所属专栏:贪心算法篇–CSDN博客 文章目录 前言例题1.买卖股票的最佳时机2.买卖股票的最佳时机23.k次取…...
oracle: 表分区>>范围分区,列表分区,散列分区/哈希分区,间隔分区,参考分区,组合分区,子分区/复合分区/组合分区
分区表 是将一个逻辑上的大表按照特定的规则划分为多个物理上的子表,这些子表称为分区。 分区可以基于不同的维度,如时间、数值范围、字符串值等,将数据分散存储在不同的分区 中,以提高数据管理的效率和查询性能,同时…...
基于SpringBoot 前端接收中文显示解决方案
一. 问题 返回给前端的的中文值会变成“???” 二. 解决方案 1. 在application.yml修改字符编码 (无效) 在网上看到说修改servlet字符集编码,尝试了不行 server:port: 8083servlet:encoding:charset: UTF-8enabled: trueforce: true2. …...
java练习(5)
ps:题目来自力扣 给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。 请你将两个数相加,并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外,这…...
python算法和数据结构刷题[3]:哈希表、滑动窗口、双指针、回溯算法、贪心算法
回溯算法 「所有可能的结果」,而不是「结果的个数」,一般情况下,我们就知道需要暴力搜索所有的可行解了,可以用「回溯法」。 回溯算法关键在于:不合适就退回上一步。在回溯算法中,递归用于深入到所有可能的分支&…...
大数据数仓实战项目(离线数仓+实时数仓)1
目录 1.课程目标 2.电商行业与电商系统介绍 3.数仓项目整体技术架构介绍 4.数仓项目架构-kylin补充 5.数仓具体技术介绍与项目环境介绍 6.kettle的介绍与安装 7.kettle入门案例 8.kettle输入组件之JSON输入与表输入 9.kettle输入组件之生成记录组件 10.kettle输出组件…...
【开源免费】基于Vue和SpringBoot的公寓报修管理系统(附论文)
本文项目编号 T 186 ,文末自助获取源码 \color{red}{T186,文末自助获取源码} T186,文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…...
使用QMUI实现用户协议对话框
使用QMUI实现用户协议对话框 懒加载用于初始化 TermServiceDialogController 对象。 懒加载 lazy var 的作用 lazy var dialogController: TermServiceDialogController {let r TermServiceDialogController()r.primaryButton.addTarget(self, action: #selector(primaryC…...
女生年薪12万,算不算属于高收入人群
在繁华喧嚣的都市中,我们时常会听到关于收入、高薪与生活质量等话题的讨论。尤其是对于年轻女性而言,薪资水平不仅关乎个人价值的体现,更直接影响到生活质量与未来的规划。那么,女生年薪12万,是否可以被划入高收入人群…...
初识Cargo:Rust的强大构建工具与包管理器
初识Cargo:Rust的强大构建工具与包管理器 如果你刚刚开始学习Rust,一定会遇到一个名字:Cargo。Cargo是Rust的官方构建工具和包管理器,它让Rust项目的创建、编译、测试和依赖管理变得非常简单。本文将带你快速了解Cargo的基本用法…...
【Windows7和Windows10下从零搭建Qt+Leaflet开发环境】
Windows7和Windows10下从零搭建QtLeaflet开发环境 本文开始编写于2025年1月27日星期一(农历:腊月二十八,苦逼的人,过年了还在忙工作)。 第一章 概述 整个开发环境搭建需要的资源: 操作系统 Windows7_x6…...
关于MySQL InnoDB存储引擎的一些认识
文章目录 一、存储引擎1.MySQL中执行一条SQL语句的过程是怎样的?1.1 MySQL的存储引擎有哪些?1.2 MyIsam和InnoDB有什么区别? 2.MySQL表的结构是什么?2.1 行结构是什么样呢?2.1.1 NULL列表?2.1.2 char和varc…...
WSL2中安装的ubuntu开启与关闭探讨
1. PC开机后,查询wsl状态 在cmd或者powersell中输入 wsl -l -vNAME STATE VERSION * Ubuntu Stopped 22. 从windows访问WSL2 wsl -l -vNAME STATE VERSION * Ubuntu Stopped 23. 在ubuntu中打开一个工作区后…...
LeetCode435周赛T2贪心
题目描述 给你一个由字符 N、S、E 和 W 组成的字符串 s,其中 s[i] 表示在无限网格中的移动操作: N:向北移动 1 个单位。S:向南移动 1 个单位。E:向东移动 1 个单位。W:向西移动 1 个单位。 初始时&#…...
π0:仅有3B数据模型打通Franka等7种机器人形态适配,实现0样本的完全由模型自主控制方法
Chelsea Finn引领的Physical Intelligence公司,专注于打造先进的机器人大模型,近日迎来了一个令人振奋的里程碑。在短短不到一年的时间内,该公司成功推出了他们的首个演示版本。这一成就不仅展示了团队的卓越技术实力,也预示着机器…...
DeepSeek-R1 低成本训练的根本原因是?
在人工智能领域,大语言模型(LLM)正以前所未有的速度发展,驱动着自然语言处理、内容生成、智能客服等众多应用的革新。然而,高性能的背后往往是高昂的训练成本,动辄数百万美元的投入让许多企业和研究机构望而…...
pandas(二)读取数据
一、读取数据 示例代码 import pandaspeople pandas.read_excel(../002/People.xlsx) #读取People数据 print(people.shape) # 打印people表的行数、列数 print(people.head(3)) # 默认打印前5行,当前打印前3行 print("") print(people.tail(3)) # 默…...
北京门头沟区房屋轮廓shp的arcgis数据建筑物轮廓无偏移坐标测评
在IT行业中,地理信息系统(GIS)是用于处理、分析和展示地理空间数据的重要工具,而ArcGIS则是GIS领域中的一款知名软件。本文将详细解析标题和描述中提及的知识点,并结合“门头沟区建筑物数据”这一标签,深入…...
【自学笔记】Java的重点知识点-持续更新
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 Java知识点概览一、Java简介二、Java基本语法三、面向对象编程(OOP)四、异常处理五、常用类库六、多线程编程七、网络编程 注意事项 总结 Ja…...
向上调整算法(详解)c++
算法流程: 与⽗结点的权值作⽐较,如果⽐它⼤,就与⽗亲交换; 交换完之后,重复 1 操作,直到⽐⽗亲⼩,或者换到根节点的位置 这里为什么插入85完后合法? 我们插入一个85,…...
