[Java]多态

news2025/2/4 5:40:01

1. 多态的基本概念

1.1 定义:

多态是指同一操作作用于不同的对象时,能够表现出不同的行为。多态通常通过以下两种方式实现:

  • 方法重载(Overloading)
  • 方法重写(Overriding)

1.2 示例:

假设有一个父类Animal,它有一个sound方法。DogCat类继承了Animal,并重写了sound方法。通过多态,可以让我们用统一的方式调用sound方法,但不同的对象会有不同的行为。

class Animal {
    void sound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Dog barks");
    }
}

class Cat extends Animal {
    @Override
    void 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 {
    @Override
    void sound() {
        System.out.println("Dog barks");
    }
}

class Cat extends Animal {
    @Override
    void sound() {
        System.out.println("Cat meows");
    }
}

在这个例子中,DogCat类重写了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 barks

        Animal 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 {
    @Override
    void 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 -> Animal

        animal.sound();  // 输出:Dog barks
    }
}

解释

  • dogDog 类型的对象。
  • 通过 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
    }
}

解释

  • animalAnimal 类型的引用,它实际上指向一个 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 {
    @Override
    public void sound() {
        System.out.println("Dog barks");
    }
}

class Cat implements Animal {
    @Override
    public void sound() {
        System.out.println("Cat meows");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal1 = new Dog();
        animal1.sound();  // 输出:Dog barks

        Animal 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())。
  • 即使 DogAnimal 的子类,它并没有自动继承 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());  // 输出:10

        Box<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 {
    @Override
    public 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 {
    @Override
    void sound() {
        System.out.println("Dog barks");
    }
}

class Cat extends Animal {
    @Override
    void sound() {
        System.out.println("Cat meows");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal1 = new Dog();
        animal1.sound();  // 输出:Dog barks

        Animal 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 {
    @Override
    public 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 barks

        Animal cat = new Cat();
        cat.sound();  // 输出:Animal makes a sound
    }
}

17. 多态与集合框架

在Java集合框架中,接口和多态结合非常紧密。例如,ListSetMap等接口允许不同的实现类(如ArrayListHashSet等)被统一处理,通过多态在运行时决定使用哪一种实现。

示例:

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接口通过多态允许不同类型的列表实现,如ArrayListLinkedList等,它们都可以在运行时根据需要被使用。

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

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

相关文章

Intellij 插件开发-快速开始

目录 一、开发环境搭建以及创建action1. 安装 Plugin DevKit 插件2. 新建idea插件项目3. 创建 Action4. 向新的 Action 表单注册 Action5. Enabling Internal Mode 二、插件实战开发[不推荐]UI Designer 基础JBPanel类&#xff08;JPanel面板&#xff09;需求&#xff1a;插件设…

语言月赛 202412【题目名没活了】题解(AC)

》》》点我查看「视频」详解》》》 [语言月赛 202412] 题目名没活了 题目描述 在 XCPC 竞赛里&#xff0c;会有若干道题目&#xff0c;一支队伍可以对每道题目提交若干次。我们称一支队伍对一道题目的一次提交是有效的&#xff0c;当且仅当&#xff1a; 在本次提交以前&…

MySQL锁类型(详解)

锁的分类图&#xff0c;如下&#xff1a; 锁操作类型划分 读锁 : 也称为共享锁 、英文用S表示。针对同一份数据&#xff0c;多个事务的读操作可以同时进行而不会互相影响&#xff0c;相互不阻塞的。 写锁 : 也称为排他锁 、英文用X表示。当前写操作没有完成前&#xff0c;它会…

OSCP - Proving Grounds - Roquefort

主要知识点 githook 注入Linux path覆盖 具体步骤 依旧是nmap扫描开始&#xff0c;3000端口不是很熟悉&#xff0c;先看一下 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…

集合通讯概览

&#xff08;1&#xff09;通信的算法 是根据通讯的链路组成的 &#xff08;2&#xff09;因为通信链路 跟硬件强相关&#xff0c;所以每个CCL的库都不一样 芯片与芯片、不同U之间是怎么通信的&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 很重要…

【贪心算法篇】:“贪心”之旅--算法练习题中的智慧与策略(二)

✨感谢您阅读本篇文章&#xff0c;文章内容是个人学习笔记的整理&#xff0c;如果哪里有误的话还请您指正噢✨ ✨ 个人主页&#xff1a;余辉zmh–CSDN博客 ✨ 文章所属专栏&#xff1a;贪心算法篇–CSDN博客 文章目录 前言例题1.买卖股票的最佳时机2.买卖股票的最佳时机23.k次取…

oracle: 表分区>>范围分区,列表分区,散列分区/哈希分区,间隔分区,参考分区,组合分区,子分区/复合分区/组合分区

分区表 是将一个逻辑上的大表按照特定的规则划分为多个物理上的子表&#xff0c;这些子表称为分区。 分区可以基于不同的维度&#xff0c;如时间、数值范围、字符串值等&#xff0c;将数据分散存储在不同的分区 中&#xff0c;以提高数据管理的效率和查询性能&#xff0c;同时…

基于SpringBoot 前端接收中文显示解决方案

一. 问题 返回给前端的的中文值会变成“???” 二. 解决方案 1. 在application.yml修改字符编码 &#xff08;无效&#xff09; 在网上看到说修改servlet字符集编码&#xff0c;尝试了不行 server:port: 8083servlet:encoding:charset: UTF-8enabled: trueforce: true2. …

java练习(5)

ps:题目来自力扣 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外&#xff0c;这…

python算法和数据结构刷题[3]:哈希表、滑动窗口、双指针、回溯算法、贪心算法

回溯算法 「所有可能的结果」&#xff0c;而不是「结果的个数」&#xff0c;一般情况下&#xff0c;我们就知道需要暴力搜索所有的可行解了&#xff0c;可以用「回溯法」。 回溯算法关键在于:不合适就退回上一步。在回溯算法中&#xff0c;递归用于深入到所有可能的分支&…

大数据数仓实战项目(离线数仓+实时数仓)1

目录 1.课程目标 2.电商行业与电商系统介绍 3.数仓项目整体技术架构介绍 4.数仓项目架构-kylin补充 5.数仓具体技术介绍与项目环境介绍 6.kettle的介绍与安装 7.kettle入门案例 8.kettle输入组件之JSON输入与表输入 9.kettle输入组件之生成记录组件 10.kettle输出组件…

【开源免费】基于Vue和SpringBoot的公寓报修管理系统(附论文)

本文项目编号 T 186 &#xff0c;文末自助获取源码 \color{red}{T186&#xff0c;文末自助获取源码} T186&#xff0c;文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

【Windows7和Windows10下从零搭建Qt+Leaflet开发环境】

Windows7和Windows10下从零搭建QtLeaflet开发环境 本文开始编写于2025年1月27日星期一&#xff08;农历&#xff1a;腊月二十八&#xff0c;苦逼的人&#xff0c;过年了还在忙工作&#xff09;。 第一章 概述 整个开发环境搭建需要的资源&#xff1a; 操作系统 Windows7_x6…

关于MySQL InnoDB存储引擎的一些认识

文章目录 一、存储引擎1.MySQL中执行一条SQL语句的过程是怎样的&#xff1f;1.1 MySQL的存储引擎有哪些&#xff1f;1.2 MyIsam和InnoDB有什么区别&#xff1f; 2.MySQL表的结构是什么&#xff1f;2.1 行结构是什么样呢&#xff1f;2.1.1 NULL列表&#xff1f;2.1.2 char和varc…

WSL2中安装的ubuntu开启与关闭探讨

1. PC开机后&#xff0c;查询wsl状态 在cmd或者powersell中输入 wsl -l -vNAME STATE VERSION * Ubuntu Stopped 22. 从windows访问WSL2 wsl -l -vNAME STATE VERSION * Ubuntu Stopped 23. 在ubuntu中打开一个工作区后…

π0:仅有3B数据模型打通Franka等7种机器人形态适配,实现0样本的完全由模型自主控制方法

Chelsea Finn引领的Physical Intelligence公司&#xff0c;专注于打造先进的机器人大模型&#xff0c;近日迎来了一个令人振奋的里程碑。在短短不到一年的时间内&#xff0c;该公司成功推出了他们的首个演示版本。这一成就不仅展示了团队的卓越技术实力&#xff0c;也预示着机器…

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)) # 默…

向上调整算法(详解)c++

算法流程&#xff1a; 与⽗结点的权值作⽐较&#xff0c;如果⽐它⼤&#xff0c;就与⽗亲交换&#xff1b; 交换完之后&#xff0c;重复 1 操作&#xff0c;直到⽐⽗亲⼩&#xff0c;或者换到根节点的位置 这里为什么插入85完后合法&#xff1f; 我们插入一个85&#xff0c;…

LabVIEW无线齿轮监测系统

本案例介绍了基于LabVIEW的无线齿轮监测系统设计。该系统利用LabVIEW编程语言和改进的天牛须算法优化支持向量机&#xff0c;实现了无线齿轮故障监测。通过LabVIEW软件和相关硬件&#xff0c;可以实现对齿轮箱振动信号的采集、传输和故障识别&#xff0c;集远程采集、数据库存储…

力扣第435场周赛讲解

文章目录 题目总览题目详解3442.奇偶频次间的最大差值I3443.K次修改后的最大曼哈顿距离3444. 使数组包含目标值倍数的最少增量3445.奇偶频次间的最大差值 题目总览 奇偶频次间的最大差值I K次修改后的最大曼哈顿距离 使数组包含目标值倍数的最少增量 奇偶频次间的最大差值II …