Java开发 | 重写 | 多态
创始人
2025-05-31 02:18:13

前言

大家好,我是程序猿爱打拳,今天给大家带来的是面向对象之封装继承多态中的多态。文章从多态的概念、多态的条件、重写、向上向下转型、多态的优缺点等来详细讲解多态,让我们继续学习面向对象的过程吧。

目录

1.多态的概念

2.多态的实现条件

3.重写

4.向上转型和向下转型

4.1向上转型

4.2向下转型

5.多态的优缺点

5.1避免繁琐的操作

5.2可扩展能力强

5.3多态缺点

6.避免在构造方法中调用重写的方法


1.多态的概念

多态以我们通俗的话来说,就是一种事物用多种形态来实现。在Java当中就是完成某个行为时用不同的对象去完成会出现不同情况。

一台打印机,当黑白打印机去实现打印图片功能时打印出来的是黑白图片。而彩色打印机实现打印图片功能时打印出来的是彩色图片。这就是一个功能,不同的实现,也是多态的思想。 


2.多态的实现条件

在Java当中要实现多态,必须要满足以下几个条件:

  1. 必须在继承的体现下实现
  2. 子类必须要对父类中的方法进行重写
  3. 通过父类的引用调用重写的方法

多态的体现,在代码的运行时,由于传递的参数不同,会调用不同的类。

class Animal {public void eat() {System.out.println("吃饭啦!");}
}
class Dog extends Animal {@Overridepublic void eat() {System.out.println("吃狗粮啦!");}
}
class Cat extends Animal {@Overridepublic void eat() {System.out.println("吃猫粮啦!");}
}
public class Test {public static void main(String[] args) {Animal an1 = new Dog();Animal an2 = new Cat();Animal an3 = new Animal();an1.eat();an2.eat();an3.eat();}
}

运行后输出: 

以上程序满足,多态的几个基本条件。实现的就是一个简单的重写,也就是父类和子类有相同的方法。下面我就来具体讲解重写的用法。


3.重写

重写(override):也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。

重写的满足条件:

  1. 参数列表相同
  2. 方法名相同
  3. 返回类型相同

【重写与重载的区别】

区别重写(oberride)重载()
参数列表一定不能被修改必须修改
返回类型一定不能修改除非可以构成父子类关系可以修改
访问限定修饰符一定不能修改更严格的限制可以修改

早期的QQ只能发信息,而今天的QQ在发信息的过程中可以看到对方的状态如正在输入信息、正在听歌等等,这是一个动态绑定的思想。

静态绑定:也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代表函数重载。
动态绑定:也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体调用那个类的方法。典型代表函数重写。


4.向上转型和向下转型

4.1向上转型

向上转型实际上就是创建一个子类对象,将其当作父类来使用。语法格式为:父类类型 对象名 = new 子类类型();

class Animal {public String name;public void eat() {System.out.println(name+"吃饭啦!");}
}
class Dog extends Animal {@Overridepublic void eat() {System.out.println(name+"吃狗粮啦!");}
}
class Cat extends Animal {@Overridepublic void eat() {System.out.println(name+"吃猫粮啦!");}
}
public class Test {public static void main(String[] args) {Animal an1 = new Dog();Animal an2 = new Cat();
}

在上述代码中的main方法中,an1和an2就是一个子类对象当作父类类型来使用,也就是向上转型的一个体现,因此

当我们的 编译器中出现上图左侧这样的标志时表示着我们的代码向上转型了,是重写的体现标志。向上转型的使用场景为:直接赋值、方法传参、方法返回

(1)直接赋值

class Animal {public String name;public int age;public Animal(String name, int age) {this.name =name;this.age = age;}public void eat() {System.out.println(name+"吃饭啦!");}
}
class Dog extends Animal {public Dog(String name,int age) {super(name,age);}@Overridepublic void eat() {System.out.println(name+"吃狗粮啦!");}
}
class Cat extends Animal {public Cat(String name,int age) {super(name,age);}@Overridepublic void eat() {System.out.println(name+"吃猫粮啦!");}
}
public class Test {public static void main(String[] args) {Animal an1 = new Dog("旺财",3);Animal an2 = new Cat("小眯",2);an1.eat();an2.eat();}
}

运行后输出: 

以上代码展示了直接赋值的效果,在main方法中直接在实例化Dog和Cat时,把参数传给各个子类,达到了向上转型的效果。


(2)方法传参

class Animal {public String name;public int age;public Animal(String name, int age) {this.name =name;this.age = age;}public void eat() {System.out.println(name+"吃饭啦!");}
}
class Dog extends Animal {public Dog(String name,int age) {super(name,age);}@Overridepublic void eat() {System.out.println(name+"吃狗粮啦!");}
}
class Cat extends Animal {public Cat(String name,int age) {super(name,age);}@Overridepublic void eat() {System.out.println(name+"吃猫粮啦!");}
}
public class Test {//通过方法传参public static void eatFood(Animal str) {str.eat();}public static void main(String[] args) {//直接赋值Animal an1 = new Dog("旺财",3);Animal an2 = new Cat("小眯",2);eatFood(an1);eatFood(an2);}
}

运行后输出: 

以上代码中的main方法,我添加了一个eatFood方法。在程序运行前这个方法是里面的str只是一个引用,程序运行后会一次把main方法中的an1和an2传给str,因此达到了一个引用实现两个对象的效果。这样也可以达到个向上转型。

因此,当父类引用的对象不一样时表现出的行为是不一样的,这就是多态的思想。


4.2向下转型

当我们将一个子类经过向上转型后当成父类的方法使用时,再无法调用子类方法了。当时当我们需要调用子类方法中的特性时,就会形成一个向下转型的效果。

class Animal {public String name;public int age;public Animal(String name, int age) {this.name =name;this.age = age;}public void eat() {System.out.println(name+"吃饭啦!");}
}
class Dog extends Animal {public Dog(String name,int age) {super(name,age);}@Overridepublic void eat() {System.out.println(name+"吃狗粮啦!");}
}
class Cat extends Animal {public Cat(String name,int age) {super(name,age);}@Overridepublic void eat() {System.out.println(name+"吃猫粮啦!");}
}
public class Test {public static void main(String[] args) {//直接赋值Animal an1 = new Dog("旺财",3);Animal an2 = new Cat("小眯",2);Animal an3 = new Animal("阿虎",4);//向下转型,输出了子类的特性an1.eat();//向下转型,输出了子类的特性an2.eat();//输出了父类自己的特性an3.eat();}
}

运行后输出: 

以上代码main方法中,所有的引用都是父类中的eat()方法。但是Dog和Cat有自己的特性,因此父类根据子类特有的特性输出了Dog和Cat这两个子类中的eat()方法,这就是向下转型的体现。

重写的注意项:

  1. private修饰的方法不能重写
  2. static修饰的方法不能重写
  3. 子类的访问修饰限定权限要大于等于父类的权限
  4. final修饰的方法不能被重写,我们称之密封方法

5.多态的优缺点

假设有以下代码,画图形:

class Shape {public void draw() {System.out.println("画图型");}
}class Rect extends Shape {@Overridepublic void draw() {System.out.println("画菱形");}
}class Cycle extends Shape {@Overridepublic void draw() {System.out.println("画圆形");}
}class Flower extends Shape {@Overridepublic void draw() {System.out.println("画花儿");}
}
public class Test {public static void main(String[] args) {Rect rect = new Rect();Cycle cycle = new Cycle();Flower flower = new Flower();String[] sharpes = {"cycle","rect","flower"};for (String shape: sharpes) {if (shape.equals("cycle")) {cycle.draw();}else if(shape.equals("rect")) {rect.draw();}else if(shape.equals("flower")) {flower.draw();}}}
}

运行后输出: 

我们发现这样去编写代码,非常的繁琐,要不停的用法if elseif等等,当我们使用多态后,就能避免这样繁琐的操作。


5.1避免繁琐的操作

class Shape {public void draw() {System.out.println("画图型");}
}class Rect extends Shape {@Overridepublic void draw() {System.out.println("画菱形");}
}class Cycle extends Shape {@Overridepublic void draw() {System.out.println("画圆形");}
}class Flower extends Shape {@Overridepublic void draw() {System.out.println("画花儿");}
}
public class Test {public static void main(String[] args) {Shape[] shapes = {new Rect(),new Cycle(),new Flower()};for (Shape shape: shapes) {shape.draw();}}
}

运行后输出: 

 我们发现,使用多态后能够降低代码的 "圈复杂度", 避免使用大量的 if - else分支语句。


5.2可扩展能力强

如果我们要新增一种新的形状,使用多态的方式代码的改动成本也比较低,如增加一个三角形:

class Triangle extends Shape {@Overridepublic void draw() {System.out.println("画三角形");}
}public class Test {public static void main(String[] args) {Shape[] shapes = {new Rect(),new Cycle(),new Flower(),new Triangle()};for (Shape shape: shapes) {shape.draw();}}
}

我们只需要新增一个类和增加一个对象即可。 


5.3多态缺点

  1. 代码的运行效率降低
  2. 属性没有多态性,当父类和子类都有同名属性的时候,通过父类的引用只能引用父类自己的成员属性
  3. 构造方法没有多态性,见下方讲解

6.避免在构造方法中调用重写的方法

class A {public A() {func();}public void func() {System.out.println("A的func");}
}
class B extends A {private int num = 10;@Overridepublic void func() {System.out.println("B的func"+num);}
}
public class Test {public static void main(String[] args) {B b = new B();}
}

运行后输出: 

构造方法B对象的同时,会调用A的构造方法。A的构造方法中调用了 func 方法, 此时会触发动态绑定, 会调用到B中的func此时 B 对象自身还没有构造, 此时 num 处在未初始化的状态, 值为 0. 如果具备多态性,num的值应该是1.所以在构造函数内,尽量避免使用实例方法,除了final和private方法。


那么本期博客都这里就结束了,感谢各位的耐心阅读。

下期预告:抽象类与接口

相关内容

热门资讯

Mysql常用数据类型总结 整形 枚举类型ENUE整形       TINYINT,SMALLINT,MEDIUMINT,IN...
【flink sql】创建表 flink sql创建表语法 CREATE TABLE [IF NOT EXISTS] [catal...
python opencv 保... 👨‍💻个人简介: 深度学习图像领域工作者 dz...
Pytorch深度学习实战3-... 目录1 数据集Dataset2 数据加载DataLoader3 常用预处理方法4 模型处理5 实例&...
自定义类型的超详细讲解ᵎᵎ了解...   目录 1.结构体的声明 1.1基础知识 1.2结构体的声明 1.3结构体的特殊声明  1.4结构...
Docker等容器技术如何与移... 移动应用程序的开发面临着很多挑战,包括开发环境的设置、测试的困难、部署的复杂性等。由于...
【微服务】—— Nacos安装... 文章目录1. Windows安装1.1 下载安装包1.2 解压1.3 端口配置1.4 启动1.5 访...
【OpenGL】 为了理解这个函数我们需要先学习一些OpenGL的内容 OpenGL可视化 https://g...
hjr-详细说一下Redis集... Redis作用 缓存 一般我们用Redis做缓存,热点数据 击穿:访问到...
【蓝桥杯】 C++ 数字三角形... 文章目录题目描述输入描述输出描述实现代码解题思路注意点知识点 题目描述 上图给出了一个数字三角形。从...
VR全景展会丨探索未来,重塑现... 随着科技的不断发展,虚拟现实(VR)技术逐渐成为一个重要的...
C++数据类型 目录 C++基础数据类型 指针 指针类型 指针赋值 引用 参考:《深...
超实用!!! 三分钟将你的项目... 文章目录前言一、在项目中新增配置二、配置github page setting?三、如...
数据结构---队列 专栏:数据结构 个人主页:HaiFan. 专栏简介:这里是...
数字操作方法 系列文章目录 前端系列文章——传送门 JavaScript系列文章——传送门 文章目录系列文章目录...
Cartesi 2023 年 ... 查看 Cartesi Machine、Cartesi Rollups 和 Noether 的更新正在...
JavaWeb——jsp概述入... JSP定义:  在如下一个jsp文件里面有如下的代码  <%@ page content...
一切喜怒哀乐都来自于你的认知 01 有个学子,准备出国,父母请来清华的教授宁向东。请问教授࿱...
JAVA并发编程——synch... 引言         Java语言为了解决并发编程中存在的原子性、可见性和有序性问题,...
git学习----3.21 未... 文章目录前言Git :一个分布式版本控制工具目标一、概述1.1 开发中的实际场景1.2...
Qt优秀开源项目之十七:QtP... QtPromise是Promises/A+规范的Qt/C++实现。该规范的译...
【前端八股文】JavaScri... 文章目录Set概念与arr的比较属性和方法并集、交集、差集Map概念属性和方法String用索引值和...
海康硬盘录像机接入RTSP/o... EasyNVR安防视频云服务平台可支持设备通过RTSP/Onvif协议接入平台,能提供...
在混合劳动力时代如何避免网络安... 在混合劳动力时代如何避免安全网络风险 三年多来,混合工作一直是工作生活中不可或缺的一...
2023还不懂Jmeter接口... 这里介绍的Jmeter接口测试的的实战,如果文章内容没遇看懂的话,我这边...
基于4G/5G弱网聚合的多链路... 基于4G/5G多卡聚合(弱网聚合)的智能融合通信设备技术亮点 增强带宽提供可靠连接 通过将多个有线和...
如何使用Synplify综合v... 文章目录使用Synplify综合的好处synplify的教程方法1(无效)...
2023年全国最新高校辅导员精... 百分百题库提供高校辅导员考试试题、辅导员考试预测题、高校辅导员考试真题、辅导员证考试题库等ÿ...
2022年18个值得期待的Le... 有数百个独特的LearnDash附加组件,您可能很难选择您的LearnDash LMS...
【java基础】Stream流... 文章目录基本介绍流的创建流的各种常见操作forEach方法filter方法map方法peek方法fl...