【JavaSE系列】 第九节 —— 多态那些事儿

news2024/9/28 7:21:29

文章目录

前言

一、多态的概念

二、向上转型和向下转型 

2.1 向上转型

2.2 什么是向上转型

2.3 三种常见的向上转型

2.3.1 直接赋值

2.3.2 作为方法的参数

2.3.3 作为方法的返回值 

2.4 向下转型(这个了解即可)

三、方法重写 

3.1 方法重写的概念

3.2 方法重写的规定 

3.3 在IDEA中使用重写的快捷方式

3.4 方法重写中所要注意的细节

四、多态 

4.1 什么是多态

 4.2 多态产生的前提

五、理解多态含义

六、多态的优缺点

6.1 使用多态的好处

6.2 多态的缺点

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

总结


前言

今天我们将进入到多态的学习,在上一章中我们学习了有关继承的概念,今天我们学习Java的第二个重要特性多态,本节内容也十分重要,我们应该认真学习并且掌握;


一、多态的概念

多态,从语文的层次上来说:一种事务,多种形态;这句话不算对,但也不算错;

但是,我们需要从程序的角度上来介绍:去完成某个行为,当不同的对象去完成时,会产生不同的状态,这就是多态。

举例说明:

比如说,如下图所示:同样是一个打印机,去打印相同的照片;但是,交给彩色打印机打印出来的就是彩色的照片;交给黑白打印机打印出来的就是黑白照片;它们完成的动作都是“打印”;这就是一种多态;

再比如说,如下图所示,去“吃饭”,对于小猫来说,吃的是“猫粮”;但对于小狗来说,吃的确是“狗粮”;他们完成的都是“吃饭”这个行为,但是却“吃出不同的结果来”; 这也是一种多态;

                                                                                                                                                                                                       

 总的来说,同一件事情,发生在不同的对象上,会产生不同的结果。 

总结:

那么,要想真正了解多态,我们还是需要从三个方面来介绍:

  1. 什么是向上转型;
  2. 什么叫做方法重写;
  3. 了解了前两个,我们才会真正了解什么是多态。

下面我们依次介绍

二、向上转型和向下转型 

2.1 向上转型

2.2 什么是向上转型

首先介绍一段平平常常的继承代码:

package Demo1;
class Animal {
    public String name;
    public int age;
 
    public void eat() {
        System.out.println(this.name+"吃饭!");
    }
}
 
class Cat extends Animal {
    public String hair;
    public void mew() {
        System.out.println(this.name+"正在叫!");
    }
}
 
public class TestDemo1 {
 
    public static void main(String[] args) {
        Cat cat = new Cat();
        cat.mew();
    }
}

  那么,如果现在抛开 继承 不谈,直接用Animal类 new一个animal对象,可以发现,animal对象访问不了Animal类 里面没有的成员变量 或成员方法:

 接下来,可以来讲一讲 向上转型 的知识了:

       这里的 上 指的是 父类,那么 下 指的就是 子类;

那么,把子类给父类是什么意思呢?

        //即:定义类一个 cat
        //可以用animal来接收
 
        //也就是说,父类的引用 可以用来引用 子类的对象
 
        Cat cat = new Cat();
        Animal animal = cat;
 
        //也就是说,上面的两行代码,可以合并成下面一行代码
        
        Animal animal = new Cat();
   
        //此时,父类的引用 引用了 子类的对象,我们把这个就叫做 向上转型

但是,此时又有一个新的问题;

       animal的类型是Animal,那么 此时它只能去访问 类Animal 的成员变量和方法,去访问 子类Cat的成员变量或方法的时候会报错:

 【总结】

向上转型,把原来的 子类的类型 转换成了 父类的类型,那么,就只能去访问 父类特有的成员方法或者成员变量。

2.3 三种常见的向上转型

2.3.1 直接赋值

所谓直接赋值,就是 上面的直接把 子类对象 给 父类 进行引用:

        /*
        Cat cat = new Cat();
        Animal animal = cat;
        */
 
 
        Animal animal = new Cat();

2.3.2 作为方法的参数

2.3.3 作为方法的返回值

2.4 向下转型(这个了解即可)

前面已经介绍过 向上转型,那么 现在来介绍一下 向下转型:

 不过,现在来执行一下这样的操作:

 此时,运行结果:

但是,向下转型不安全(不介意使用向下转型):

  我们还需要做以下修改 以保证其安全性:

【注意】

 

三、方法重写 

  由上面可知,父类引用引用了子类的对象;但是,在现实生活中,猫是吃猫粮的;

那么,如果想改的话,肯定不可以在父类上面进行修改的;毕竟,可能还有 其他的子类 来继承父类;

那么,如果想修改的话,就需要在子类里面重新取实现一遍这个方法的: 

    然后,我们来对比一下 实现前后的结果:

没有在子类里面写eat方法:

 在子类里面写了eat方法:


                                                                                                                                                                                                       
这是怎么回事呢?

这就是马上所要介绍的方法重写。

3.1 方法重写的概念

重写,也称为覆盖;

重写,是子类对父类 非静态、非private修饰、非final修饰、非构造方法 等的实现过程进行重新编写;

重写的好处是:子类可以根据需要,定义特定的属于自己的行为;如 上面的猫可以吃猫粮,狗可以吃狗粮。

3.2 方法重写的规定 

方法重写满足以下三个条件:

  1. 方法的名称相同
  2. 方法的返回值相同
  3. 方法的参数列表相同

当在子类 方法重写以后,那么就会调用的是 子类重写的内容。

我们把这个现象叫做动态绑定(这是多态的基础)

       在上面所示例中,

       在编译的时候,调用的还是 父类Animal的eat方法;

       但是,在运行的时候,变成了子类Cat自己的eat方法;

       因此,动态绑定又称为 运行时绑定

       即:在程序运行的时候才知道要调用谁。

 当然,有了 动态绑定,那肯定也有 静态绑定:

  在编译期间就已经知道了 要调用的是谁,比如说 重载。

3.3 在IDEA中使用重写的快捷方式

当然,在使用IDEA编译器的时候 ,

重写不仅仅可以直接在子类上手敲出来的(上面的就是),而且还可以使用快捷键的方式:

快捷键步骤;

3.4 方法重写中所要注意的细节

1.静态方法(static修饰)是构成不了重写的:

                                                                                                                                                                                                       

2.private修饰 的方法不能进行重写:

                                                                                                                                                  

3.如果要进行重写的话,那么 子类的 访问限定修饰符的权限 一定 大于等于 父类的访问限定修饰符: 

访问限定符权限大小比较:

private < default < peotected < public  

                                                                                                                                                                                                       

4.被final修饰的方法不可以进行重写:

                                                                                                                                                 

5.

子类和父类在同一个包中,那么子类可以重写父类的所有方法(除了 声明为private和final的方法);子类和父类不在同一个包,那么子类只能够 重写父类的 声明为public和protected的非final的方法(即 默认权限方法/包访问权限 不可以被重写);

6.重写的方法,可以使用 @Override 注解来显示指定;有了这个注解 可以帮助我们进行一些合法性校验;如 不小心把方法名字写错了(写成ate),那么此时编译器就会发现父类中没有ate方法,就会编译报错,提示无法构成重写。

四、多态 

4.1 什么是多态

类的实现者所写的代码:

class Animal {
 
    public String name;
    public int age;
    public void eat() {
        System.out.println(this.name+"吃饭!父类Animal");
    }
}
 
class Cat extends Animal {
    public String hair;
 
    public void eat(){
        System.out.println(this.name+"吃猫粮!");
    }
    public void mew() {
        System.out.println(this.name+"正在叫!");
    }
}
class Dog extends Animal {
        public void eat(){
            System.out.println(this.name+"吃狗粮!");
    }
}

类的调用者所写的代码:

    public static void function(Animal animal) {
        animal.eat();
    }
 
    public static void main(String[] args) {
        Cat cat = new Cat();
        Dog dog = new Dog();
        function(cat);
        function(dog);
    }

所以运行之后得到的结果不一样:

                                                                                                                                                  

       从上面可以得到,同一个方法,当引用的对象不一样的时候,这个方法所表现出来的行为是不一样的;我们把这种思想就叫做 多态。 

 4.2 多态产生的前提

  1. 发生向上转型:父类引用 引用子类的对象;
  2. 发生重写:父类和子类当中 有同名的覆盖方法;
  3. 通过父类引用,调用这个重写的方法,此时会发生 动态绑定

思想:通过一个引用调用一个方法,由于引用的对象不同,所执行的行为也是不一样的;我们把这种思想就叫做多态的思想。

五、理解多态含义

回顾一下多态:

场景:现在想画一个图形(图形是未知的):

首先,创建父类:

class Shape {
    //省略了长、宽、高等之类的属性
    public void draw(){
        System.out.println("画图形!!!!!!");
    }
 
}

创建父类Shape只是画父类,但是并没有说明画什么;

现在想画各种各样的图形,那么就可以去重写 Shape类里面的draw方法来满足自己的需求:

class Cycle extends Shape {
    @Override
    public void draw() {
        System.out.println("○");
    }
}
 
class Rect extends Shape {
    @Override
    public void draw() {
        System.out.println("  ⃟ ");
    }
}
 
class Triangle extends Shape {
    @Override
    public void draw() {
        System.out.println("△");
    }
}

前面两段代码都是类的实现者写的;

当有一天作为用户、作为类的调用者 想要画出这些图形,那么就可以这样来做:

    public static void drawMap(Shape shape) {
        shape.draw();
    }
    public static void main(String[] args) {
        Cycle cycle = new Cycle();
        Rect rect = new Rect();
        Triangle triangle = new Triangle();
        
        drawMap(cycle);
        drawMap(rect);
        drawMap(triangle);
    }

那么,根据引用的对象不一样,draw方法所表现的行为就不一样

这个就叫做 多态。

代码示例结果:

那么,多态到底有什么好处呢?

——其拓展能力非常强:

如果突然说,现在想要画一朵花:

那么只需要在这样做即可:

class Flower extends Shape {
    @Override
    public void draw() {
        System.out.println("✿");
    }
}

测试类添上这个来测试:

drawMap(new Flower());

代码示例结果:

六、多态的优缺点

6.1 使用多态的好处

(1)能够降低代码的 "圈复杂度" ,避免使用大量的 if-else语句;

 说白了,可以简单粗暴的计算 一段代码中条件语句和循环语句 出现的个数,这个个数就称为 "圈复杂度";如果一个方法的 圈复杂度 太高,就需要考虑重构。

(2)可扩展能力强; 

就是上面所说的 添加了一朵花的 示例。

6.2 多态的缺点

   代码的运行效率低。 

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

 
class B {
    public B(){
        func();
    }
 
    public void func() {
        System.out.println("B.func() ");
    }
}
 
class D extends B {
    D(){
        super();
    }
    @Override
    public void func() {
        System.out.println("D.func() ");
    }
}
 
public class Test {
    public static void main(String[] args) {
        D d = new D();
    }
}

代码示例结果:

【说明】

  1. 构造D对象的同时,会调用B的构造方法;
  2. B的构造方法中调用了 func方法,此时会触发 动态绑定,会调用到D中的 func;

【注意】最好尽量不要写类似的代码——避免在构造方法中调用重写的方法。

 


总结

今天我们关于Java多态的内容就介绍到这里,到这里我们JavaSE的内容就结束了。一定要熟练掌握有关JavaSE的基础,这是我们学习Java最基本的内容。

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

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

相关文章

启动golang项目编译的exe可执行文件获取windows管理员权限(UAC)

背景&#xff1a; go代码启动以后里面涉及到修改ip地址等操作&#xff0c;需要管理员权限。打包好的exe文件双击执行默认是没有管理员权限的&#xff0c;那么修改ip就会提示需要管理员权限。 解决方法1&#xff1a;右键以管理员权限运行exe文件 解决方法2&#xff1a;编译exe…

前端jQuery ajax请求,后端node.js使用cors跨域

前言 跨域&#xff0c;一句话介绍&#xff1a; 你要请求的URL地址与当前的URL地址&#xff0c;协议不同、域名不同、端口不同时&#xff0c;就是跨域。 步入正题 前端&#xff0c;jQuery ajax请求 $.ajax({async: false,method: post,//URl和端口与后台匹配好&#xff0c;当…

点云分割预研

文章目录激光雷达点云分割1.点云分割主流方案&#xff08;模型角度&#xff09;1.1 (a) 基于RGB-D图像1.2 (d) 基于点云1.3 (b) 基于投影图像1.4 (b) 基于投影图像 - SqueezeSeg/RangeNet1.4. 球映射2 点云分割主流方案&#xff08;部署角度&#xff09;3 点云分割常用指标4 点…

MySQL 02 :三层结构、备份删除数据库

MySQL 02 &#xff1a;数据库三层结构-破除MySQL神秘 请添加图片描述 通过golang操作MySQL 创建删除数据库 备份恢复数据库 第一次需要配置环境&#xff0c;否则会报错 报错&#xff1a;mysqldump: Got error: 1045: Access denied for user ‘root’‘localhost’ (using …

HDLBits: 在线学习 SystemVerilog(二十三)-Problem 158-162(找BUG)

HDLBits: 在线学习 SystemVerilog&#xff08;二十三&#xff09;-Problem 158-162&#xff08;找BUG&#xff09;HDLBits 是一组小型电路设计习题集&#xff0c;使用 Verilog/SystemVerilog 硬件描述语言 (HDL) 练习数字硬件设计~网址如下&#xff1a;https://hdlbits.01xz.ne…

【LeetCode】《LeetCode 101》第二章:最易懂的贪心算法

文章目录2.1 算法解释2.2 分配问题455. 分发饼干 &#xff08;简单&#xff09;135. 分发糖果 &#xff08;困难&#xff09;2.3 区间问题435. 无重叠区间&#xff08;中等&#xff09;2.4 练习605. 种花问题&#xff08;简单&#xff09;452. 用最少数量的箭引爆气球&#xff…

FFmpeg介绍及入门知识

1、简介 FFmpeg是一套由c语言编写的&#xff0c;可以用来记录、转换数字音频、视频&#xff0c;并能将其转化为流的开源计算机程序,自身采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案&#xff0c;包含了非常先进的音频/视频编解码库libavcodec&#xf…

【Unity Android Platform:关于Android权限来源(安卓)如何查找】

Android权限来源 问题描述&#xff1a;当项目接入的插件逐渐变多&#xff0c;不仅仅是AndroidManifest会影响Android Permission的个数&#xff0c;甚至有些API也会影响最终的权限个数&#xff0c;例如下图所示&#xff1a; 当国内权限问题涉及到一些安全隐私问题时&#xff0…

构造函数与普通函数,显式原型与隐式原型,原型与原型链

原型与原型链1 学前先了解一些概念1.1 构造函数和普通函数的区别1.1.1 调用方式1.1.2 函数中this的指向不同1.1.3 写法不同1.2 问题明确2 原型与原型链2.1 原型2.2 显式原型与隐式原型2.3 原型链3 原型链环形结构1 学前先了解一些概念 1.1 构造函数和普通函数的区别 构造函数…

全流程基于最新导则下的生态环境影响评价技术方法及图件制作与案例

目录 专题一、生态环境影响评价框架及流程 专题二、基于遥感解译的土地利用现状图的编制 专题三、生物多样性测定及R语言分析 专题四、植被类型及植被覆盖度图的编制 专题五、生物量与净初级生产力测定&#xff1a;实测及模型 专题六、生态系统类型及服务价值评估 专题七…

MOT学习笔记 — 行人检测及行人跟踪数据集总结

1. 行人红外数据集总结 &#xff08;1&#xff09;OSU Thermal Pedestrian Database 下载链接&#xff1a;http://vcipl-okstate.org/pbvs/bench/Data/01/download.html &#xff08;2&#xff09;IRIS Thermal/Visible Face Database 下载链接&#xff1a;http://vcipl-o…

React 服务端渲染

React 服务器端渲染概念回顾什么是客户端渲染CSR(Client Side Rendering)服务器端只返回json数据&#xff0c;Data和Html的拼接在客户端进行&#xff08;渲染&#xff09;。什么是服务器端渲染SSR(Server Side Rendering)服务器端返回数据拼接过后的HTML&#xff0c;Data和Html…

Ubuntu20.04下安装vm17+win10/11

一、安装vmware17 1、官网下载 vmware官网&#xff1a;https://www.vmware.com/cn/products/workstation-pro/workstation-pro-evaluation.html 2、安装依赖 sudo apt update sudo apt install build-essential linux-headers-generic gcc make3、权限和安装 到下载的目录下…

vector你得知道的知识

vector的基本使用和模拟实现 一、std::vector基本介绍 1.1 常用接口说明 std::vector是STL中的一个动态数组容器&#xff0c;它可以自动调整大小&#xff0c;支持在数组末尾快速添加和删除元素&#xff0c;还支持随机访问元素。 以下是std::vector常用的接口及其说明&#xf…

品牌软文怎么写?教你几招

软文是什么&#xff1f;软文的本质就是广告&#xff0c;当然不是明晃晃的推销&#xff0c;而是自然隐晦地植入产品信息&#xff0c;引导更多用户自愿下单。 品牌软文对于写手的经验、内容的质量要求都相对较高&#xff0c;否则写出来的软文无法达到预期的效果。品牌软文怎么写…

一个古老的html后台的模板代码

效果图下&#xff1a; css部分代码&#xff1a;/* CSS Document / body{font-family:“宋体”, Arial,Verdana, sans-serif, Helvetica;font-size:12px;margin:0;background:#f4f5eb;color:#000;} dl,ul,li{list-style:none;} a img{border:0;} a{color:#000;} a:link,a:visit…

[css]通过网站实例学习以最简单的方式构造三元素布局

文章目录二元素布局纵向布局横向布局三元素布局b站直播布局实例左右-下 布局左-上下 布局上下-右 布局方案一方案二后言二元素布局 在学习三元素布局之前&#xff0c;让我们先简单了解一下只有两个元素的布局吧 两个元素的相对关系非常简单&#xff0c;不是上下就是左右 纵向布…

Anaconda配置Python科学计算库SciPy的方法

本文介绍在Anaconda环境中&#xff0c;安装Python语言SciPy模块的方法。 SciPy是基于Python的科学计算库&#xff0c;用于解决科学、工程和技术计算中的各种问题。它建立在NumPy库的基础之上&#xff0c;提供了大量高效、易于使用的功能&#xff0c;包括统计分析、信号处理、优…

用一个例子告诉你 怎样在spark中创建累加器

目录 1.说明 1.1 什么是累加器 1.2 累加器的功能 2. 使用累加器 3. 累加器和reduce、fold算子的区别 1.说明 1.1 什么是累加器 累加器是Spark提供的一个共享变量(Shared Variables) 默认情况下&#xff0c;如果Executor节点上使用到了Driver端定义的变量(通过算子传…

Redis常用命令及数据类型参数

1. 针对于string SET key value / GET key SET k1 v1 GET k1 // v1String是二进制安全的&#xff0c;是可变长度的&#xff0c; 底层类似于ArrayList 是可扩容的&#xff0c;最大存储内存为 512MB。 2. 判断key中是否存在某个内容 EXISTS key SET k1 v1 EXISTS k1 // …