news 2026/9/3 8:46:39

32面向对象(中级)-多态

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
32面向对象(中级)-多态

1.多态的基本介绍

多态 = 多种状态

同一个方法,对于不同的对象,会做出不同反应(结果),就是多态。

(1) 生活版:什么是多态?

①你有一个宠物喂食器喂食器只认识:动物(父类)

你往里面放:

同一个喂食器,同一个喂食动作:

  • 喂狗 → 狗汪汪吃
  • 喂猫 → 猫喵喵吃
  • 喂猪 → 猪哼哼吃

②同样一句 “付款”,微信支付会跳微信、支付宝会跳支付宝、银行卡会走银联 —— 动作都是 “付款”,但具体实现完全不一样。

同一个动作,不同对象,表现不一样→ 这就叫多态

(2) 翻译成 Java 代码(最简单版本)

父类:动物(通用)

class Animal { // 动物都会叫 public void shout() { System.out.println("动物叫"); } }

子类 1:狗

class Dog extends Animal { // 狗重新叫:汪汪 @Override public void shout() { System.out.println("汪汪汪"); } }

子类 2:猫

class Cat extends Animal { // 猫重新叫:喵喵 @Override public void shout() { System.out.println("喵喵喵"); } }

(3)重点来了:多态写法

Animal a = new Dog(); a.shout(); Animal a = new Cat(); a.shout();

这句话人话翻译

  • 左边Animal a:我声明这是一只动物

  • 右边new Dog():但我实际放进去的是

也就是:表面是动物,实际是狗

运行结果:

汪汪汪 喵喵喵

变量 a 是 Animal 类型,但它先执行的是狗的方法,后执行的是猫的方法,这就是多态

2.多态的前提(必须同时满足)

  1. 有继承关系(extends)

  2. 子类重写父类方法(override)

  3. 父类引用指向子类对象Animal a = new Dog();

3. 多态的核心口诀

编译看左边(是什么类型),运行看右边(实际是谁)

  • 编译时:看父类有没有这个方法
  • 运行时:执行子类的方法
//animal编译类型就是Animal,运行类型 Dog Animal a = new Dog(); //因为运行时 ,执行到该行时,animal运行类型是Dog,所以cry就是Dog的cry a.shout()://小狗汪汪叫
  • 左边:Animal 编译类型(编译看它)
  • 右边:Dog 运行类型(运行用它)

等号左边也叫编译类型,右边叫运行类型

4.多态的细节

(1)向上转型

(2)向下转型

前面不能调用子类的特有成员(方法),但是就是想调用,那怎么办呢?

eg:

(Cat)animal 把animal转成了Cat类。

第三点解读:要求父类的引用必须指向的是当前目标类型的对象:animal 原本就是指向 Cat 类型的对象 cat,就是再向上转型的时候 Animal animal = new Cat();

(3)属性没有重写之说,属性的值看编译类型。

(4)instanceof 比较操作符,用于判断(在需要判断的时候)对象的运行类型是否为XX类型 或 XX类型的子类型

5.为什么 Java 一定要有多态?

多态不是凭空设计出来的语法,它本质是为了解决软件开发里最头疼的三个痛点:代码冗余、耦合太重、扩展困难

我们用最常见的「支付场景」对比一下,你立刻就能懂。

痛点 1:没有多态,代码里全是 if-else,臃肿又难维护

假设你要做一个收银功能,支持微信、支付宝、银行卡三种支付方式。 如果不用多态,你的代码大概率会写成这样:

// 没有多态的糟糕写法 public void pay(String payType, double money) { if ("wechat".equals(payType)) { WeChatPay weChat = new WeChatPay(); weChat.doPay(money); } else if ("alipay".equals(payType)) { AliPay ali = new AliPay(); ali.doPay(money); } else if ("bank".equals(payType)) { BankPay bank = new BankPay(); bank.doPay(money); } }

这种写法的问题非常明显:

  • 每新增一种支付方式(比如数字人民币),你都要跑到这个方法里加一行else if,代码越堆越长;
  • 支付逻辑和调用逻辑强绑在一起,改一个支付方式,可能影响整个方法。

用多态怎么解决?—— 用「抽象接口」统一行为

多态的思路是:先定义一个抽象的 “支付” 能力,再让各个支付方式自己去实现这个能力。调用方只认这个抽象能力,不认具体的支付类。

// 1. 定义抽象的支付接口(统一行为标准) interface Payment { void pay(double money); } // 2. 各个支付方式各自实现,内部逻辑互不干扰 class WeChatPay implements Payment { @Override public void pay(double money) { System.out.println("微信支付:" + money + "元"); } } class AliPay implements Payment { @Override public void pay(double money) { System.out.println("支付宝支付:" + money + "元"); } }

这时候调用方的代码会变得极其简洁:

// 调用方只和接口打交道,完全不用管具体是哪种支付 public void checkout(Payment payment, double money) { payment.pay(money); }

痛点 2:没有多态,改一处动全身,耦合太重

试想:如果哪天微信支付的方法名从doPay()改成了executePay(),你不仅要改WeChatPay类,还要把所有调用doPay()的地方全改一遍。

这就叫耦合:调用方和具体的实现类绑死了。

而多态做的最关键的一件事,就是解耦

调用方只依赖「抽象的接口 / 父类」,不依赖「具体的子类」。

就像老板让员工 “做一份报表”,他不用管你是用 Excel 做还是用 Python 写,只要你能交出报表就行。老板只依赖 “员工会做报表” 这个抽象能力,不依赖 “张三用 Excel 做报表” 这个具体实现。

对应到代码里:

  • 上层业务代码只认识Payment接口;
  • 底层把WeChatPay换成AliPay,上层checkout方法一行都不用改。

痛点 3:没有多态,加新功能必改老代码,风险极高

软件开发里唯一不变的就是变化:今天加微信支付,明天加支付宝,后天加数字人民币。

没有多态时,每加一个新功能,你都要去修改原有的业务代码。改老代码就意味着可能引入新 bug,原本好好的功能,改完可能就崩了。

而多态完美符合软件开发的「开闭原则」:

对扩展开放,对修改关闭。

新增支付方式时,你只需要新建一个类,实现Payment接口就行:

// 新增数字人民币支付,原有代码一行都不用动 class DcPay implements Payment { @Override public void pay(double money) { System.out.println("数字人民币支付:" + money + "元"); } }

原有收银逻辑、调用逻辑完全不用修改,自然也就不会引入新 bug。

※※6.java的动态绑定机制(作用域问题)

(1)当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定

(2)当调用对象属性时,没有动态绑定机制,哪里声明,那里使用

0314_韩顺平Java_动态绑定机制_哔哩哔哩_bilibili

7.多态的应用

(1)多态数组

数组的定义类型为父类类型,里面保存的实际元素类型为子类类型

//Person 是 Student ,Teacher 的父类 Person[] persons = new Person[5]; persons[0] = new Person("jack",20); persons[1] = new Student("mike", 18, 100); persons[2] = new Student("smith",19,30.1); persons[3] = new Teacher("scott",30,20000); persons[4] = new Teacher("king",50,25000);

(2)多态参数

方法定义的形参类型为父类类型,实参类型允许为子类类型

0317_韩顺平Java_多态参数_哔哩哔哩_bilibili

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/3 8:45:32

TradingView图表库本地化集成指南:从官方集成到开源替代方案

简介:本资源为TradingView前端功能离线分析包,面向量化交易学习者、技术分析初学者及Web前端开发者,用于本地研究其图表渲染逻辑、指标交互机制与UI组件结构。压缩包共676个文件,含324个JavaScript脚本(实现图表引擎、…

作者头像 李华
网站建设 2026/9/3 8:44:52

流氓软件卸载不了怎么办?从安全模式到注册表清理的三步实操

电脑里有些软件,明明点了卸载,进度条走完了,过两天又悄悄出现在桌面,开机先蹦个弹窗,想关都关不利索。这类事我自己碰上过不止一次,身边也有好几个朋友来问,说流氓软件卸载不了怎么办。后来我把…

作者头像 李华
网站建设 2026/9/3 8:44:21

10D立体环绕音效制作全解析:从音源分离到声场构建

最近在整理音乐资源时,发现很多朋友对高品质音效的现场表演视频特别感兴趣,尤其是那种能够带来沉浸式听觉体验的立体环绕音效。本文将以(G)I-DLE的《Gimme Dat Love》表演视频为例,完整解析如何实现10D立体环绕音效的补档制作,从音…

作者头像 李华
网站建设 2026/9/3 8:43:09

KALI LP-6 V2监听音箱评测:3000元价位桌面音频的性价比之选

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/3 8:41:29

6. Java 方法

方法的定义方法是一段封装特定功能的、可重复调用的代码块。语法格式:修饰符 返回值类型 方法名(参数类型 形参1, 参数类型 形参2, ...) {方法体return 返回值; }各组成部分说明:组成部分说明修饰符控制访问权限和调用方式,如 public static返…

作者头像 李华
网站建设 2026/9/3 8:40:49

AI - 阿里云百炼

阿里云百炼是阿里云 2023 年 10 月上线的一站式 MaaS 大模型开发 & 应用全链路平台,集合模型调用、微调、RAG 知识库、Agent 智能体、低代码应用搭建、API 服务,个人 / 企业开发者通用。 官网地址:大模型服务平台百炼控制台 一、核心定…

作者头像 李华