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.mzph.cn/news/828220.shtml

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

相关文章

flutter ios Firebase 消息通知错误 I-COR000005,I-FCM001000 解决

*前提是已经 使用firebase-tools 已经给 Flutter 加入了 消息通知相关配置。教程>> 一、I-COR000005 10.22.0 - [FirebaseCore][I-COR000005] No app has been configured yet. import Firebase....FirebaseApp.configure() 10.22.0 - [FirebaseMessaging][I-FCM001000…

mysql事故复盘: 单行字节最大阈值65535字节(原创)

背景 记得还在银行做开发,投产上线时,项目发版前,要提DDL的sql工单,mysql加1个字段,因为这张表为下游数据入湖入仓用的,长度较大。在测试库加字段没问题,但生产库字段加不上。 先说结论 投产…

QT初识

通过图形化界面输出helloworld 既然学习了QT,那么自然要做经典的输出helloworld字符串的实验。 QT有两好几种方案输出helloworld,一种是通过图形化界面输出,一种是通过代码实现。 这里先了解图形化界面的方案。 创建项目后,点…

LeetCode:2385. 感染二叉树需要的总时间(DFS Java)

目录 2385. 感染二叉树需要的总时间 题目描述: 实现代码与解析: DFS 原理思路: 2385. 感染二叉树需要的总时间 题目描述: 给你一棵二叉树的根节点 root ,二叉树中节点的值 互不相同 。另给你一个整数 start 。在第…

【论文阅读】互连网络的负载平衡路由算法 (RLB RLBth)

前言Oblivious Load Balancing 不经意路由负载平衡 1. oblivious routing 不经意/无关路由的背景知识 1. oblivious routing, adaptive routing & minimal/non-minimal routing algorithms 2. Balancing a 1-Dimensional ring: RLB and RLBth 一维 ring 的 RLB and RLBth 1…

如何设计一个安全的系统架构?

本文转自 公众号 ByteByteGo,如有侵权,请联系,立即删除 如何设计一个安全的系统架构? 如何设计安全的系统?我们总结了 12 条原则供架构师们参考。 设计安全的系统非常重要,原因有很多,从保护敏…

SpringCloud系列(13)--Eureka服务名称修改和服务IP显示

前言:在上一章节中我们把服务提供者做成了集群,而本章节则是一些关于服务信息的配置,这部分知识对集群整体影响不大,不过最好还是掌握,毕竟万一有用到的地方呢 1、修改服务的名称 有时候我们想要修改服务的名称&#…

JavaSE学习文档(上)

JavaSE学习文档 第一章 Java概述1.2 计算机编程语言1.3 Java语言版本概述1.4 Java语言分类1.5 JDK,JRE,JVM的关系1.6 JDK安装1.7 DOS命令1.8 Java程序执行过程1.9 编写HelloWorld1.10 常见错误1.11 编写程序时要注意的点 第二章 Java基础语法2.1 Java中的注释文档注释 2.2 关键…

武汉星起航:亚马逊全球资源赋能中国卖家,跨境电商助力品牌国际化

亚马逊全球开店业务于2015年正式进驻中国,为中国卖家打开了通往全球市场的便捷之门。这一举措不仅为中国卖家提供了与全球消费者直接交流的机会,更借助亚马逊的丰富资源和先进技术,帮助卖家将优质的中国商品推向世界舞台。亚马逊平台以其高效…

Ant Design Vue + js 表格计算合计

1.需要计算的数量固定&#xff08;如表1&#xff0c;已知需要计算的金额为&#xff1a;装修履约保证金 装修垃圾清运费出入证工本费 出入证押金 这四项相加&#xff0c;可以写成固定的算法&#xff09;&#xff1a; 表格样式&#xff1a; <h4 style"margin: 0 0 8px…

Bayes判别示例数据:鸢尾花数据集

使用Bayes判别的R语言实例通常涉及使用朴素贝叶斯分类器。朴素贝叶斯分类器是一种简单的概率分类器&#xff0c;基于贝叶斯定理和特征之间的独立性假设。在R中&#xff0c;我们可以使用e1071包中的naiveBayes函数来实现这一算法。下面&#xff0c;我将通过一个简单的示例展示如…

《生成式AI导论》学习笔记

1.课程定位 2.什么是生成式人工智慧&#xff1f; 3. 今日的生成式人工智慧厉害在哪里&#xff1f; 4.训练不了人工智慧&#xff1f;那我训练自己 5.训练不了人工智慧&#xff1f;你可以训练你自己&#xff08;中&#xff09;——拆解问题使用工具 6.大语言模型修炼史——第一阶…

微信小程序使用echarts组件实现饼状统计图功能

微信小程序使用echarts组件实现饼状统计图功能 使用echarts实现在微信小程序中统计图的功能&#xff0c;具体的实现步骤思路可进我主页查看我的另一篇博文https://blog.csdn.net/weixin_45465881/article/details/138171153进行查看&#xff0c;本篇文章主要使用echarts组件实…

Redis(六) Set集合类型

文章目录 前言命令SADDSMEMBERSSISMEMBERSCARDSPOPSMOVESREM集合间操作SINTERSINTERSTORESUNIONSUNIONSTORESDIFFSDIFFSTORE 命令小结 内部编码使用场景 前言 集合类型也是保存多个字符串类型的元素的&#xff0c;和列表类型不同的是&#xff0c;set集合类型中的元素是无序的且…

java 抽象类(abstract)

1 由abstract修饰的类叫做抽象类 也可以修饰抽象方法 2 abstract修饰的抽象方法不可以在抽象类当中实现 但一定要在子类当中重写 并实现 public abstract class p1 { public abstract void work(); public void run() { System.out.println("run"); } } class prog…

基于springboot+vue的民法普及系统的设计与实现

1、系统演示视频&#xff08;演示视频&#xff09; 2、需要请联系

2. 多机多卡运行nccl-tests对比分析

系列文章 第2章 多机多卡nccl-tests 对比分析 目录 系列文章前言一、本地环境1. 网卡接口2. RDMA3. TOPO信息pcie信息nvidia-smi topo -m 二、nccl-test对比分析1. 相关环境变量2. 不同情况的对比3. 总结与分析 前言 NCCL&#xff08;NVIDIA Collective Communications Libra…

javaWeb项目-房屋房租租赁系统功能介绍

项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1a;Vue、ElementUI 关键技术&#xff1a;springboot、SSM、vue、MYSQL、MAVEN 数据库工具&#xff1a;Navicat、SQLyog 1、JSP技术 JSP(Jav…

实战技巧:Android 14适配从挂号到出院

公众号「稀有猿诉」 原文链接 实战技巧&#xff1a;Android 14适配从挂号到出院 啥&#xff1f;这都4202年了&#xff0c;你的应用还没有升级到targetSDK 34&#xff1f;莫慌&#xff0c;本文就带着你全面的了解升级targetSDK 34的方法以及避坑指南。 注意&#xff0c;A…

毫米波雷达模块在高精度人体姿态识别的应用

人体姿态识别是计算机视觉领域中的重要问题之一&#xff0c;具有广泛的应用前景&#xff0c;如智能安防、虚拟现实、医疗辅助等。毫米波雷达技术作为一种无需直接接触目标就能实现高精度探测的感知技术&#xff0c;在人体姿态识别领域具有独特的优势。本文将探讨毫米波雷达模块…