Java之多态

一、多态前言

1.为什么要使用多态

Java中使用多态的主要目的是提高代码的可重用性和扩展性,使得代码更加灵活和易于维护。通过多态,我们可以将不同的对象看做是同一种类型,从而使得我们可以使用同一种接口来操作这些对象,而不必关心具体的实现细节。

2.多态概念

当父类的引用所指向的子类对象引用指向的对象不一样时。调用重写的方法,所表现出来的行为是不一样的,我们把这种思想叫做多态。上面所说的可能大家会觉得有点抽象,看到后面就懂了。
多态的基础是动态绑定,所以要了解多态前提我们还要了解动态绑定。
要想实现动态绑定,需要满足以上几个条件:
1.要发生向上转型
2.要发生重写
3.使用父类对象的引用去调用重写方法
完成了这三部分,就会发生动态绑定,而在这里,出现了重写以及向上转型这些概念。所以我们得先了解它们才能去了解动态绑定。进而了解多态。

二、重写

1.重写的概念

重写 (override) :也称为覆盖。将父类的方法重新在子类中使用。 返回值和形参都不能改变 即外壳不变,核心重写! 重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。

方法重写的规则:
1.子类在重写父类的方法时,必须与父类方法原型一致:即返回值、方法名、参数列表要完全一致
2.被重写的方法的访问修饰限定符在子类中要大于等于父类的。
3.父类中被static或private或final修饰的方法以及构造方法都不能被重写。 
4.在子类中重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验。

2.重写的作用

对于已经投入使用的类,尽量不要进行修改。最好的方式是:重新定义一个新的类,来重复利用其中共性的内容,并且添加或者改动新的内容。
例如:若干年前的手机,只能打电话,发短信,来电显示只能显示号码,而今天的手机在来电显示的时候,不仅仅可以显示号码,还可以显示头像,地区等。在这个过程当中,我们不应该在原来老的类上进行修改,因为原来的 类,可能还在有用户使用 ,正确做法是: 新建一个新手机的类,对来电显示这个方法重写就好了,这样就达到了我 们当今的需求了

三、向上转型

向上转型:实际就是创建一个子类对象,将其当成父类对象来使用。
语法格式:父类类型 对象名
= new 子类类型 ()
Animal animal = new Cat ( );
我们对以上代码进行实质化分析,以上的代码其实是省略化了,见以下代码

Dog dog = new Dog();
 Animal animal = dog;//该代码发生了向上转换,将Dog对象转换为Animal类型

通过向上转型后,就可以父类对象名来访问子类的方法了。使用animal.eat();这语句来访问
这个语句发生了动态绑定(在编译过程中调用的其实是父类的eat,但是在运行时换为调用子类的eat了)故实现了 创建一个子类对象,将其当成父类对象来使用。见以下代码

class Animal {

void sound() {

System.out.println("Animal makes a sound");

}

}

class Dog extends Animal {

 void sound() {

System.out.println("Dog barks");

}

 void fetch() {

System.out.println("Dog fetches a ball");

}

}

public class Main {

public static void main(String[] args) {

Dog dog = new Dog(); // 创建Dog对象

 Animal animal = dog; // 向上转型,将Dog对象转换为Animal类型

 animal.sound();//调用子类的覆盖方法

 // animal.fetch(); // 编译错误,因为Animal类中没有fetch方法

}

}

// 输出: Dog barks

通过以上代码发现一个问题不能调用到子类特有的方法(因为编译时调用的是父类的方法),我们可以通过向下转型来调用到子类特有的方法(后面介绍)
静态绑定:也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代表函数重载。
动态绑定:也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体调用那个类的方法。

向上转型 使用场景
1. 直接赋值
2. 方法传参
3. 方法返回
见以下代码

public class TestAnimal {

// 2. 方法传参:形参为父类型引用,可以接收任意子类的对象

public static void eatFood(Animal a){ //因为主方法的原因使用静态方法

a.eat();  //方法传参向上转型

}

// 3. 作返回值:返回任意子类对象的实例

public static Animal buyAnimal(String var){

return new Dog();

}

public static void main(String[] args) {

Animal cat = new Cat("元宝",2); // 1. 直接赋值:子类对象赋值给父类对象

Dog dog = new Dog("小七", 1);

animal.eat();  //直接赋值向上转型

eatFood(cat);  

eatFood(dog);

Animal animal = buyAnimal("狗");  

animal = buyAnimal("猫");

animal.eat();//方法返回向上转型

}

}

向上转型的优点:让代码实现更简单灵活。
向上转型的缺陷:不能调用到子类特有的方法。

四、多态的实现

多态具体点就是去完成某个行为时,当不同的对象去完成时会产生出不同的状态。代码如下:

class Animal {

    public void eat(){

        System.out.println( "吃饭");

    }

}

 class Cat extends Animal{

    @Override //注解

    public void eat(){

        System.out.println("吃鱼~~~");

    }

}

 class Dog extends Animal {

    @Override

    public void eat(){

        System.out.println("吃骨头~~~");

    }

}

public class TestAnimal {

    public static void eat(Animal a){

        a.eat(); //两次调用该方法,但是结果却不一样

    }

    public static void main(String[] args) {

        Cat cat = new Cat();

        Dog dog = new Dog();

        eat(cat);

        eat(dog);

    }

}

//输出结果

吃鱼~~~
吃骨头~~~

此时在上述代码中当父类的引用所指向的子类对象引用指向的对象不一样时。调用重写的方法(eat),所表现出来的行为是不一样的(输出结果不一样),我们把它叫做多态。

五、向下转型

将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转换。
语法格式:子类类型 对象名
= (强制转换)父类对象名

 Dog myDog = (Dog) animal;

那么以上代码为什么要强制类型转换呢?向上转型可以不用,因为是从小范围向大范围的转换。(可以类比整型里面的强制转换),我们现在提出一个问题:什么时候都可以向下转型吗?
答案是不,在Java中,向下转型(将父类引用转换为子类引用)一般需要先进行向上转型
见以下代码

class Animal {

    void sound() {

        System.out.println("Animal的sound");

    }

    void sun() {

        System.out.println("Animal特有的sun");

    }

}

class Dog extends Animal {

    void sound() {

        System.out.println("Dog的sound");

    }

    void fetch() {

        System.out.println("Dog特有的fetches ");

    }

}

public class Mainn {

    public static void main(String[] args) {

        Animal animal = new Dog(); // 向上转型

        Dog myDog = (Dog) animal; // 向下转型

        myDog.sound(); // 输出: Dog barks,调用子类的覆盖方法

        myDog.fetch(); // 输出: Dog fetches a ball,调用子类特有的方法

        // 调用父类的方法

        myDog.sound(); // 输出: Dog barks,调用子类的覆盖方法

        myDog.sun();

    }

}

如果上面的代码没有 Animal animal = new Dog();,向下转型将报错,同时注意必须确保父类引用所指向的对象确实是子类的实例。如果父类引用所指向的对象不是子类的实例,那么即使进行了向上转型,向下转型也是不安全的:见以下代码

class Parent {}
class Child extends Parent {}
class AnotherChild extends Parent {}

public class Main {
public static void main(String[] args) {
Parent parent = new AnotherChild(); // 向上转型
 // 这里如果尝试向下转型为Child,编译器将会报错
 // Child child = (Child) parent;//不安全的向下转型
}
}

//为了演示方便,这个代码是不完整的

因此,向下转型之前,你需要确保父类引用所指向的对象确实是你要转型的子类的实例。这通常通过instanceof操作符来检查:用来判断parent是否为Child的实例,若是,返回true,否则返回false


if (parent instanceof Child) {
    Child child = (Child) parent; //
安全的向下转型
} else {
   ......                         // 不能转换为Child
}

我们最后思考一个问题:向上转型的缺陷是不能调用到子类特有的方法,那么向下转型可以调用父类特有的方法吗?是可以的,同时向下转型后不会影响向上转型的操作。见以下代码

class Animal {

    void sound() {

        System.out.println("Animal的sound");

    }

    void sun() {

        System.out.println("Animal特有的sun");

    }

}

class Dog extends Animal {

    void sound() {

        System.out.println("Dog的sound");

    }

    void fetch() {

        System.out.println("Dog特有的fetches ");

    }

}

public class Mainn {

    public static void main(String[] args) {

        Animal animal = new Dog(); // 向上转型

        Dog myDog = (Dog) animal; // 向下转型

        myDog.sound(); // 输出: Dog barks,调用子类的覆盖方法

        myDog.fetch(); // 输出: Dog fetches a ball,调用子类特有的方法

    

        myDog.sound(); // 输出: Dog barks,调用子类的覆盖方法

        myDog.sun();   //观察到向下转型过程中可以调用父类的特有的方法

        animal.sun();   //观察到向下转型后不会影响向上转型的操作

        animal.sound();

    }

}

//输出结果

Dog的sound
Dog特有的fetches 
Dog的sound
Animal特有的sun
Animal特有的sun
Dog的sound

六、多态的优缺点

待更新~~~

七、避免在构造方法中调用重写的方法

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

❤️新版Linux零基础快速入门到精通——第一部分❤️

❤️新版Linux零基础快速入门到精通——第一部分❤️ 非科班的我!Ta!还是来了~~~1. 来认识一下Linux吧!1.1 操作系统概述1.1.1 操作系统概述1.1.2 操作系统的发展史1.1.2.1 Unix1.1.2.2 Minix1.1.2.3 Linux 1.1.3 操作系统的发展 1.2 Linux初识1.2.1 Lin…

芯科科技大大简化面向无电池物联网的能量采集产品的开发

芯科科技推出其迄今最高能量效率且支持能量采集功能的无线SoC 中国,北京 – 2024年4月22日 – 致力于以安全、智能无线连接技术,建立更互联世界的全球领导厂商Silicon Labs(亦称“芯科科技”,NASDAQ:SLAB)…

CTFHub(web sql)(四)

Cookie注入 Cookie 注入的原理也和其他注入一样,只不过是将提交的参数已 Cookie 方式提交,而一般的注入是使用 GET 或者 POST 方式提交,GET 方式提交就是直接在网址后面加上需要注入的语句,POST 方式则是通过表单,GET …

网络基础(day3)建议在电脑端注册登陆观看!!!

【 理论重点】 网络是什么&#xff1f; &#xff08;网络是载体&#xff0c;目的是传输互联网中的数据&#xff0c;数据是终端产生<手机、电脑、服务器等>。&#xff09; 如何组件网络&#xff08;良性网络架构&#xff09;&#xff1f;有网络架构思维&#xff0c;得按层…

线性代数-知识点复习(面试用)

整理&#xff1a;Peter1146717850 一、向量与线性组合 向量&#xff1a;往什么方向走多么远 e.g. ( 1 2 ) \begin{pmatrix} 1 \\ 2\end{pmatrix} (12​) 向量的模&#xff1a;向量的长度 向量的加减法&#xff1a;向量对应元素相加减&#xff08;前提&#xff1a;维度相同…

面试中关于 SpringCloud 都需要了解哪些基础?

在面试中&#xff0c;对于Spring Cloud的基础知识了解是至关重要的&#xff0c;因为Spring Cloud是构建分布式系统和微服务架构的关键技术栈之一。以下是在面试中可能会涉及到的相关问题。 1. 微服务架构基础 概念理解&#xff1a;理解微服务架构的概念&#xff0c;包括服务拆…

【MySQL】A01、性能优化-结构设计与配置

1、数据库设计原则 1.1、核心原则 不在数据库做运算; cpu计算务必移至业务层; 控制列数量(字段少而精,字段数建议在20以内); 平衡范式与冗余(效率优先&#xff1b;往往牺牲范式) 拒绝3B(拒绝大sql语句&#xff1a;big sql、拒绝大事务&#xff1a;big transaction、拒绝大…

Xbox VR头盔即将推出,但它是Meta Quest的‘限量版’。

&#x1f4f3;Xbox VR头盔即将推出&#xff0c;但它是Meta Quest的‘限量版’。 微软与Meta合作推出限量版Meta Quest VR头映射Xbox风格&#xff0c;可能是Meta Quest 3或未来版本的特别定制版&#xff0c;附带Xbox控制器。这一合作是Meta向第三方硬件制造商开放其Quest VR头盔…

什么是redis服务+redis服务数据类型有哪些??

一、背景&#xff1a; 在运维工作会一定会接触到数据库服务&#xff0c;例如oracle数据库、mysql数据库、redis数据库等&#xff0c;这里要介绍的就是redis数据库。 二、什么是redis&#xff1f;&#xff1f; Redis&#xff0c;英文全称是Remote Dictionary Server&#xff08;…

GraphQL速学笔记

在学习开始前&#xff0c;我习惯先用gpt了解一个这是个什么东西&#xff1a; GraphQL是一种用于API开发的查询语言和运行时环境。它由Facebook于2012年开发并在2015年开源&#xff0c;旨在解决传统RESTful API的一些限制和缺点。 在GraphQL中&#xff0c;客户端可以通过发送查询…

STM32F103学习笔记 | 4.STM32F103芯片介绍

STM32F1入门学习将使用STM32F103C8T6开发板最小系统板。小R为什么选择它来入门呢&#xff1f;咳咳~首先&#xff0c;ST官方提供强大且易用的标准库函数&#xff0c;使得开发过程方便快捷&#xff1b;其次&#xff0c;网上的教程资料多也十分详细。所以呢&#xff0c;它对高校学…

STM32 串口打印乱码(Cubemx)

STM32 串口打印乱码&#xff08;Cubemx&#xff09; 时钟配置错误&#xff0c;CubeMX默认的外部晶振是25MHz&#xff0c;而板载的晶振为8MHzSTM32F407修改程序将外部25M晶振修改为8M&#xff08;标准库、HAL库&#xff09; 核心问题 芯片型号与晶振配置&#xff1a;使用的ST…

深入探究音视频开源库WebRTC中NetEQ音频抗网络延时与抗丢包的实现机制

目录 1、引言 2、WebRTC简介 3、什么是NetEQ&#xff1f; 4、NetEQ技术详解 4.1、NetEQ概述 4.2、抖动消除技术 4.3、丢包补偿技术 4.4、NetEQ概要设计 4.5、NetEQ的命令机制 4.6、NetEQ的播放机制 4.7、MCU的控制机制 4.8、DSP的算法处理 4.9、DSP算法的模拟测试…

Redis之路系列(5)功夫在诗外

5 拓展篇—功夫在诗外 6.0新特性 相对都比较鸡肋&#xff0c;谨慎在生产环境使用 ACL安全策略 Redis6版本推出了ACL(Access Control List)访问控制权限 的功能&#xff0c;基于此功能&#xff0c;可以设置多个用户&#xff0c;并且给每个用户单独设 置命令权限和数据权限。 …

【Linux高性能服务器编程】两种高性能并发模式剖析——领导者/追随者模式

hello &#xff01;大家好呀&#xff01; 欢迎大家来到我的Linux高性能服务器编程系列之两种高性能并发模式介绍&#xff0c;在这篇文章中&#xff0c;你将会学习到高效的创建自己的高性能服务器&#xff0c;并且我会给出源码进行剖析&#xff0c;以及手绘UML图来帮助大家来理解…

【行为型模式】中介者模式

一、中介者模式概述 中介者模式定义&#xff1a;用一个中介对象来封装一系列的对象交互&#xff0c;中介者使各对象不需要显式地相互引用&#xff0c;从而使其耦合松散&#xff0c;而且可以独立地改变它们之间的交互。中介者模式又称为调停者模式。(对象行为型模式) 中介者模式…

Web3与物联网:探索区块链如何驱动智能设备的未来

引言 在数字化快速发展的时代&#xff0c;Web3技术和物联网&#xff08;IoT&#xff09;都成为了前沿技术的代表。两者的结合正逐渐展现出无限的可能性&#xff0c;尤其是在智能设备和数据安全方面。本文将深入探讨Web3如何与物联网相结合&#xff0c;以及这种结合对未来智能设…

有效三角形的个数 ---- 双指针

题目链接 题目: 分析: 这道题的意思就是将数组的元素, 拿出三个数, 能构成三角形就是有效的判断是否能构成三角形的条件: 两边之和大于第三边, 我们只需找到三个数中最小的两个数之和是否大于第三边, 大于则可以构成三角形解法一: 暴力解法, 即找到所有的三元组, 并挨个判断,…

分布式与一致性协议之CAP(二)

CAP CAP不可能三角 CAP不可能三角是指对于一个分布式系统而言&#xff0c;一致性、可用性、分区容错性指标不可兼得&#xff0c;只能从中选择两个&#xff0c; 如图所示。CAP不可能三角最初是埃里克布鲁尔(Eric Brewer)基于自己的工程实践提出的一个猜想&#xff0c;后被塞斯吉…

【C语言 |预处理指令】预处理指令详解(包括编译与链接)

目录 一、编译与链接 1.翻译环境 -预处理 -编译 -汇编 -链接 2.执行环境 二、预定义符号 三、#define定义常量 四、#define定义宏 五、带有副作用的宏参数 六、宏替换的规则 七、 宏函数的对比 八、#和## 1.#运算符 2.##运算符 九、命名约定 十、#undef 十一、 命…
最新文章