《走进对象村6》面向对象的第三大特性——多态

news2025/1/16 13:57:28

文章目录

  • 🚀文章导读
    • 1.1 多态的概念
    • 1.2 多态的实现条件
    • 1.3 向上转型和向下转型
    • 1.4 重写
  • **面试问题:重写和重载的区别**
  • 多态的实现

🚀文章导读

在本篇文章中,将会有很多的干货和知识点掌握,希望读者慢慢耐心阅读

在本篇文章中,将详细的讲解面向对象的第三大特性,多态,与前两种特性不同,也比前两种特性稍难,前两种特性都是通过某些关键字对类进行操作,对对象进行描述,而多态是一种非常抽象的思想。在理解多态之前,需要先了解学习多态前必备的知识点:

1、在继承体系下,向上转型和向下转型

2、父类和子类方法构成重写

3、通过父类的引用调用子类的重写方法

4、动态绑定和静态绑定

理解以上四点,就能理解什么是多态!!!

1.1 多态的概念

多态概念:父类或接口定义的引用变量可以指向子类或具体实现类的实例对象。
简单点来说就是:当完成某个行为时,不同的对象去完成时会产生不同的效果!
在Java中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)

1.2 多态的实现条件

1、继承:在多态中必须存在继承关系的子类和父类

2、重写:子类对父类中的某些方法进行重新定义,在调用这些方法时,就会调用子类的重写方法

3、向上转型:在多态中,需要用父类引用指向子类对象,只有这样才能够具备通过父类调用子类中重写父类的方法

以上就是多态的实现条件,现在看不懂没关系,下面将会针对这些条件一一讲解

1.3 向上转型和向下转型

1、向上转型

向上转型其实就是把数据类型小的引用转换成数据类型大的引用,进行数据类型之间的一个转换,从而能够访问到大的数据类型中的成员方法,但是不能访问到子类特有的方法。下面用代码解释:

//父类Animal
public class Animal {
    public String name;
    public int age;
    public void eat() {
        System.out.println(name+"吃饭");
    }
}
//子类Cat
public class Cat extends Animal {
    public Cat(String name, int age) {
        super(name, age);
    }
    //定义一个cat独有的新方法miMi
    public void miMi() {
        System.out.println("喵喵");
    }
}
//TestAnimal类
public class TestAnimal {
    public static void main(String[] args) {
        Cat cat = new Cat("小黑", 12);
       Animal animal = new Animal("animal",12);
       cat.miMi();//cat可以调用属于自己的miMi方法
       cat.eat();//cat能调用eat方法,虽然子类中没有,但是它继承了父类,所以调用的是父类中的eat方法
       animal.eat();//animal可以调用属于自己的eat方法
       animal.miMi();//但animal不能调用cat引用的对象里的miMi方法,因为animal引用的是父类对象,父类对象里没有miMi这个方法
    }

}

以上是没有经过向上转型时的代码 ,下面用代码演示三种向上转型的的三种方式;

1、直接赋值

2、方法传参

3、通过返回类型

1、直接赋值
将子类的对象直接赋值给父类的引用

public class TestAnimal {
    public static void main(String[] args) {
   //父类引用 - 引用了子类对象
        Animal animal2 = new Cat("小猫",11);                 
        animal2.eat();
        animal2.miMi();//错误,没办法引用      
        //经过向上转型后,变成了父类引用 引用了子类对象,但是animal2还不能访问Cat类中的miMi()方法,那向上转型有什么用呢,不要着急,等一下讲过重写就知道了就知道了
	}
}

2、方法传参

public class TestAnimal {
    //形参用Animal类型接受
    public static void eat(Animal animal) {
        animal.eat();
    }
    public static void main(String[] args) {
        //父类引用 - 引用了子类对象
        Cat cat = new Cat("小猫",12);
        //实参是Cat类型
        eat(cat);
    }
}

在main方法中,实例化了三个对象,然后调用了TestAnimal中的eat()方法,而传参时,传入的实参是对象的引用,而实参的类型是子类,形参用的是父类来接受的,这里就相当与进行了类型转化,因为,类也是一种引用数据类型嘛!所以,在这种场景下,发生了向上转型!

当然这里会出现一个疑问,不是参数类型不同不能进行传参么,对的,类型不同不能进行传参,但是,如果是父子类关系的话,就可以进行传参!

3、通过返回类型

public class TestAnimal {
    //返回类型设置成父类Animal
    public static Animal animalMethod() {
        return new Cat("小猫",11);
    }
    public static void main(String[] args) {
        //返回的类型是Animal,所以用Animal来接受
        Animal animal = animalMethod();
        animal.eat();
    }
}

向上转型的优点:让代码的实现更加灵活;

向上转型的缺点:不能调用到子类特有的方法,只能调用到发生重写的方法;

以上就是向上转型的三种方式,请读者朋友慢慢品会。

2、向下转型

将类型大的向类型小的进行转换(不安全)

//父类
public 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("吃饭");
    }
}
//子类
public class Cat extends Animal {
    public Cat(String name, int age) {
        super(name, age);
    }
    public void miMi() {
        System.out.println("喵喵");
    }
}
//子类
public class Dog extends Animal{
    public Dog(String name, int age) {
        super(name, age);
    }
}
//测试
public class TestAnimal {
    public static void main(String[] args) {
        //发生向上转型
        Animal animal = new Cat("小猫",11);;
        animal.miMi();//无法进行调用

        //向下转型
        Cat cat = (Cat)animal;
        cat.miMi();
        
         //对狗类进行向上转型
        Animal animal1 = new Dog("小狗",12);
        //对animal向下转型
        Cat cat1 = (Cat)animal1;//运行时会抛出错误
        cat1.miMi();
    }
}

在这里插入图片描述

第一种情况:

当进行Cat进行向上转型后,通过父类引用仍然是没办法访问miMi方法,因为在Animal类中根本就没有miMi方法,所以,可以将animal进行向下转型,变成Cat类型的,因为Cat类里面有miMi方法,所以可以进行调用;

第二种情况:

当对狗类进行向上转型后,有对animal进行了向下转型,转化成了Cat类,并没有报错,但是运行时抛出了异常,因为animal本来指向的是狗类,但是非要强转成猫类,这样驴头不对马嘴,当然是不行的,所以就会报出类转换异常ClassCastException;而第一种情况是,因为animal本来指向的就是猫类,向下转型转换成猫类也是没问题的;所以向下转型是不安全的。
在这里插入图片描述

如果要进行向下转型,需要利用instanceof 作出检查,如果表达式为真,则可以安全转化;下面代码演示:

public class TestAnimal {
    public static void main(String[] args) {
        //发生向上转型
        Animal animal = new Cat("小猫",11);;
        //向下转型
        Cat cat = (Cat)animal;
        cat.miMi();
        //对狗类进行向上转型
        Animal animal1 = new Dog("小狗",12);
        //假如if语句进行检查
        if(animal1 instanceof Cat) {
            Cat cat1 = (Cat)animal1;
            cat1.miMi();
        }else {
            System.out.println("转换异常");
        }
    }
}

在这里插入图片描述

1.4 重写

概念:重写也称为覆盖,在子类中,对父类中的方法进行重新编写,等于把父类中方法复制黏贴到子类里面。

现在注意力回到刚才向上转型那里,刚刚经过向上转型后,父类引用指向了子类对象,虽然能够调用eat方法,但是调用的仍然是父类中的eat方法,那么现在,在子类中也定义一个和父类一模一样的eat方法,经过向上转型之后会发生什么变化呢?

//父类Animal
public class Animal {
    public String name;
    public int age;
    public void eat() {
        System.out.println(name+"吃饭");
    }
}
//子类Cat
public class Cat extends Animal {
    public Cat(String name, int age) {
        super(name, age);
    }
    //定义一个cat独有的新方法miMi
    public void miMi() {
        System.out.println("喵喵");
    }
    //定义一个和父类中一模一样的eat方法
        @Override
    public void eat() {
        System.out.println(name+"吃猫粮");
    }
}
//TestAnimal类
public class TestAnimal {
    //进行向上转型
    public static void method(Animal animal) {
        animal.eat();
    }
    public static void main(String[] args) {
        Cat cat = new Cat("小猫",11);
        method(cat);
    }

}

在这里插入图片描述

在这里插入图片描述

运行结果显示,虽然是父类引用,引用了子类对象,但是在调用eat方法时,并没有调用父类中的eat方法,而是通过父类引用去调用了子类中的重写了父类的eat方法,所以得出结论,因为在子类中,对父类的eat方法进行了重写,所以在调用eat方法是,会调用子类的eat方法。在下面多态的实现中还会结合例子为大家讲解;但是,在这里,还要再引出两个新的名词动态绑定和静态绑定,刚刚在通过父类引用去调用子类中重写了父类的eat方法的这个过程就是动态绑定。

动态绑定(运行时绑定)概念:即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体调用了哪个类中的方法,方法的重写就是动态绑定;

下面通过汇编代码讲解为什么称为运行时绑定

在这里插入图片描述

静态绑定概念:在编译时,根据用户所传递的实参的类型及顺序确定了具体调用哪个方法。函数的重载就是静态绑定;

重写规则:

1、在子类中被重写的方法必须与父类中的方法外壳一模一样,但可以修改方法体中的内容,外壳即修饰符、返回值、方法名、参数列表

2、重写方法的返回值可以不同,但必须是父子类关系

3、注意:父类中,被private、final、修饰的方法不能被重写,静态方法和构造方法不能被重写

4、如果父类方法被public修饰,则在子类中重写该方法时,就不能被剩余的三个修饰符修饰,所以:在重写时,访问权限不能比父类中被重写的方法的访问权限更低。

5、重写方法可以用@Overrride注解显示描述,有了这个注解能进行一些合法的校验,例如把方法名字拼错了,此时编译器就会报错,提示无法构成重写。

6、构造器不能被重写,但可以被重载

面试问题:重写和重载的区别

1、方法的重载和重写都是实现多态的方式,区别在与前者实现的是编译时的多态性,而后者实现的是运行时的多态性

2、重载:发生在同一个类中,方法名相同参数列表不同(参数类型不同、个数不同、顺序不同),与方法返回值和访问修饰符无关,即重载的方法不能根据返回类型进行区分

3、重写:发生在父子类中,方法名、参数列表必须相同,返回在可以不同,但必须是父子类关系,在子类中,重写方法的访问修饰符要大于等于父类中的访问修饰符,如果父类方法访问修饰符是private,则子类中就不是重写

多态的实现

在代码中,写了一个父类Animal ,和子类Dog、子类Cat、子类Bird;它们分别都继承了父类Animal;所以继承方式是多个类继承同一个类;多说无益,下面将一步一步的讲解->

//父类Animal
public 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+"吃饭");
    }
    public void eat() {
        System.out.println(name+"吃饭");
    }
}
//子类cat  
public class Cat extends Animal {
    public Cat(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat() {
        System.out.println(name+"吃猫粮");
    }
}
//子类Dog
public class Dog extends Animal{
    public Dog(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat() {
        System.out.println(name+"吃狗粮");
    }
}
//子类Bird
public class Bird extends Animal{
    public Bird(String name, int age) {
        super(name, age);
    }
       @Override
    public void eat() {
        System.out.println(name+"吃鸟粮");
    }
}
//类TestAnimal用于测试
public class TestAnimal {
    public static void eat(Animal animal) {
        animal.eat();
    }
    public static void main(String[] args) {
        Cat cat = new Cat("小猫", 12);
        Dog dog = new Dog("小狗", 13);
        Bird bird = new Bird("小鸟", 14);
        //因为eat()方法是用static修饰的,所以不用通过引用来调用eat()方法
        eat(cat);//传入对象的引用
        eat(dog);
        eat(bird);
    }
}

在这里插入图片描述

当传入的对象的引用不同时,通过animal所调用的eat方法就会发生不一样的效果,从而就验证了:当要完成一个行为时,不同的对象去完成时,产生的效果也不同!

在这里插入图片描述

你品,你细品!!!

再来看这张图,实例化了三个子类对象,分别调用了三次eat方法,将三个不同的引用分别作为实参传给了型参,而形参的类型是父类Animal类型,所以发生了向上转型,所以当animal引用的是Cat对象时,调用的就是子类Cat里面的eat()方法,当animal引用的是Dog对象时,调用的就是Dog里面的eat()方法,当animal引用的是Bird对象时,调用的就是Bird里面的eat()方法;所以这样就完成了通过父类去调用子类中的重写方法;当然,这是其中一种理解方式;还有第二种理解方式:当在main方法中调用eat方法时,传入一个cat引用,因为animal的类型是Animal,所以进行了向上转型,又因为,父类中的eat和子类Cat中的eat方法发生了重写,所以子类Cat中的eat方法覆盖了父类中的eat方法,所以在调用时,调用了被覆盖的父类中的eat方法!!!

所以只要能在继承的条件下发生向上转型,就能发生动态绑定,能发生动态绑定,就可以实现多态!

以上就是关于多态的讲解,多态是一种思想,需要我们慢慢的去品汇,去理解,如果觉得本篇文章不错,还望留下一个小小小的👍哟

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

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

相关文章

如何设定项目中的里程碑?

项目管理中非常重要的就是合理设置阶段性的里程碑,在项目实施过程中,根据里程碑来灵活控制项目进度和节奏。那么一个IT项目该如何合理地安排里程碑呢? 在IT项目管理中,里程碑是一种非常重要的工具,它能够帮助项目经理和…

谈谈架构分层

大家好,我是易安! 在系统从0到1的阶段,为了让系统快速上线,我们通常是不考虑分层的。但是随着业务越来越复杂,大量的代码纠缠在一起,会出现逻辑不清晰、各模块相互依赖、代码扩展性差、改动一处就牵一发而动…

IIS6.0和网络管理软件SNMPc

一.IIS6.0 IIS 6.0提供了更为方便的安装/管理功能和增强的应用环境、基于标准的分布协议、改进的性能表现和扩展性,以及更好的稳定性和易用性。其服务组件包括: ①WWW服务。WWW是图形最为丰富的Internet服务。Web具有很强的链接能力,支持协…

软考中级——系统集成项目管理工程师(20天冲刺)

PV:计划价值(计划要成的价值) EV:挣值(实际做了的事儿的价值) AC:实际成本(实际花出去多少钱) SV:进度偏差EV-PV(项目提前或者落后的进度)>0项目进度超前<0项目进度落后 CV:成本偏差EV-AC(项目预算的号空成者盈利)>0成本节约<0成本超支 SPI:进度绩效指数EV/PV(挣值…

创建VUE2 前端以及后端的交互

创建vue2项目 1.javascript–>vue(不要勾选)–>安装element-ui(组件 | Element)–>执行指令&#xff08;npm i element-ui -S&#xff09;–>在main.js中引入&#xff08;import ElementUI from ‘element-ui’; ​ import ‘element-ui/lib/theme-chalk/index.c…

JavaWeb:Web 的基本概念、Tomcat 服务器、Http 详解、Maven 的下载安装步骤、模仿一个 Servlet

文章目录 JavaWeb - 01一、基本概念1、静态 Web2、动态 Web3、Web 应用程序4、三个技术 二、Web 服务器三、Tomcat 详解四、发布一个 Web 网站五、Http 详解1. Http 请求&#xff08;1&#xff09;请求行&#xff08;2&#xff09;消息头 2. Http 响应&#xff08;1&#xff09…

sourceTree离线环境部署

目录 1、下载sourceTree安装包&#xff0c;打开之后弹出注册界面&#xff08;需要去国外网站注册&#xff09;2、使用技术手段跳过注册步骤3、打开安装包进行安装 注&#xff1a;建议提前安装好git 1、下载sourceTree安装包&#xff0c;打开之后弹出注册界面&#xff08;需要去…

27 - 两数、三数、四数问题

文章目录 1. 两数之和2. 三数之和3. 最接近的三数之和4. 四数之和5. 四数相加 1. 两数之和 在遍历数组的时候只需要在map中去查询是否有个目前元素target - numbers[i]匹配的数值&#xff0c;如果有&#xff0c;就找到匹配对&#xff0c;如果没有就把目前遍历的元素放入map中&a…

融合有序,创造无限——解密力扣“合并两个有序数组”

本篇博客会讲解力扣“88. 合并两个有序数组”这道题&#xff0c;这是题目链接。 其实&#xff0c;有经验的朋友一看到这个题目的名字&#xff0c;应该就明白了&#xff0c;这玩意和归并排序脱不了干系。下面我们来审题&#xff1a; 输出示例如下&#xff1a; 以下是一些提…

3.是人就能学会的Spring源码教学-IOC容器的核心实现原理

是人就能学会的Spring源码教学-IOC容器的核心实现原理 我们学习Spring源码的动力&#xff0c;估计大部分人都是因为面试中会问到吧。 那么我们今天就以面试问Spring来开头。 关于Spring&#xff0c;在面试的时候一般会问到的两个最基础的问题。 第一个什么是IOC&#xff1f…

技术控,看这里,一款支持断点调试的数据科学工具

数据科学是一门利用统计学、机器学习、数据挖掘、数据可视化等技术和方法&#xff0c;从数据中提取知识和信息的交叉学科。自上世纪60年代&#xff0c;统计学家John W.Tukey首次提出“数据分析”&#xff08;Data Analysis&#xff09;的概念起&#xff0c;数据科学已历经了几十…

ASEMI代理ADUM131E1BRWZ-RL原装ADI车规级ADUM131E1BRWZ-RL

编辑&#xff1a;ll ASEMI代理ADUM131E1BRWZ-RL原装ADI车规级ADUM131E1BRWZ-RL 型号&#xff1a;ADUM131E1BRWZ-RL 品牌&#xff1a;ADI /亚德诺 封装&#xff1a;SOIC-16-300mil 批号&#xff1a;2023 安装类型&#xff1a;表面贴装型 引脚数量&#xff1a;16 工作温度…

基于springboot的“智慧食堂”设计与实现(源码等)

摘要 随着Internet的发展&#xff0c;人们的日常生活已经离不开网络。未来人们的生活与工作将变得越来越数字化&#xff0c;网络化和电子化。网上管理&#xff0c;它将是直接管理“智慧食堂”系统的最新形式。本xx是以构建“智慧食堂”系统为目标&#xff0c;使用java技术制作…

智能仿写软件-智能伪原创改写软件

智能仿写工具&#xff1a;营销创意的必备利器 在当今快节奏和不断发展的商业环境中&#xff0c;企业营销人员需要在短时间内产生大量有创意和高质量的内容。因此&#xff0c;智能仿写工具作为营销策略的一种创新方法而出现&#xff0c;可以帮助企业的写作团队更快速地生成文章…

【软考数据库】第八章 数据库SQL语言

目录 8.1 SQL语言概述 8.2 数据库定义 8.2.1 创建表(create table) 8.2.2 修改表 (alter table) 8.2.3 删除表 (drop table) 8.2.4 索引 8.2.5 视图 8.3 数据操作 8.3.1 查询语句格式 8.3.2 分组查询 8.3.3 其他操作 8.3.4 约束 8.4 数据授权 8.4.1 授权grant 8…

数据库索引的原理,为什么要用 B+树,为什么不用二 叉树?

1、B树和B树 一般来说&#xff0c;数据库的存储引擎都是采用B树或者B树来实现索引的存储。首先来看B树&#xff0c;如图所士 B树是一种多路平衡树&#xff0c;用这种存储结构来存储大量数据&#xff0c;它的整个高度会相比二叉树来说&#xff0c;会矮很多。 而对于数据库而言…

RFID系统在物流仓储中的应用

RFID系统是一种无线识别技术&#xff0c;最近成为物流仓储行业的热门话题。本文将介绍RFID系统在物流仓储中的应用&#xff0c;包括如何使用RFID标签进行物流管理&#xff0c;如何使用RFID技术提高仓库的安全性&#xff0c;并细述RFID技术在物流仓储中的优势。除此之外&#xf…

ArrayList快速失败机制

文章目录 一、什么是快速失败机制二、例子三、底层原理四、解决方法五、快速失败机制的一个小bug 一、什么是快速失败机制 ArrayList实现了一种称为快速失败(fail-fast)的机制,该机制在并发修改时会抛出ConcurrentModificationException异常。 这种机制的实现原理是:ArrayList…

机器学习案例 | 通过EBG学习概念cup

基于解释的学习(explanation-basedlearning)可简称为解释学习&#xff0c;是20世纪80年代中期开始兴起的一种机器学习方法。解释学习根据任务所在领域知识和正在学习的概念知识&#xff0c;对当前实例进行分析和求解&#xff0c;得出一个表征求解过程的因果解释树&#xff0c;以…

spark2

18Spark中stage的划分 和 shuffle的概念 Stage的划分是根据宽依赖&#xff0c;当触发action算子时&#xff0c;按照从后往前的回溯算法&#xff0c;当遇到会发生shuffle算子的时候&#xff0c;就会切分stage。 Stage的划分本质是shuffle,即当遇到会发生shuffle算子的时…