「JavaSE」类和对象3

🎇个人主页:Ice_Sugar_7
🎇所属专栏:快来卷Java啦
🎇欢迎点赞收藏加关注哦!

类和对象3

  • 🍉多态
    • 🍌重写
    • 🍌向上转型&向下转型
    • 🍌静态绑定&动态绑定
    • 🍌多态的利弊
  • 🍉写在最后

🍉多态

概念:对于同一个行为,不同的对象去做,会产生不同的状态
比如对于吃这个行为,狗这个对象去做的话就是吃狗粮;猫去做的话就是吃猫粮
再比如,对于景区买票这个行为,学生去做的话就是买学生票;儿童去做的话就是买儿童票;成人去做的话就是买成人票

java中的多态指同一个方法可以根据接收的不同参数类型,产生不同的行为
要实现多态,必须同时满足以下几个条件:

  • 子类必须要对父类中方法进行重写
  • 必须在继承体系下实现向上转型
  • 通过父类的引用调用重写的方法

下面对这些条件一一讲解

🍌重写

继承关系上,如果满足:

  • 方法名一样
  • 方法的参数列表一样

那我们就说这两个方法之间的关系是重写
举个例子:

public class Animal {public String name;public int age;public void eat() {System.out.println(name+"正在吃饭");}
}public class Dog extends Animal{public String color;@Overridepublic void eat() {System.out.println(name+"在吃狗粮");}
}

父类中有一个eat方法,而子类中也有一个,不同点在于它们所吃的东西不同(一个吃饭、一个吃狗粮)

重写的规则:

  • 被重写的父类方法不能被private、final修饰,并且不是构造方法、静态方法
  • 被重写的方法返回值类型可以不同,但必须具有父子关系
  • 子类方法的访问权限不能低于父类中被重写方法的访问权限。比如父类方法被public修饰,那子类中重写的方法就不能声明为 protected
  • 重写的方法可以使用 @Override 注解来显式指定,它能帮我们进行校验,确保顺利重写

重写和重载的区别:
在这里插入图片描述

🍌向上转型&向下转型

一、向上转型
创建一个子类对象,但是用父类引用来引用它
语法格式:父类类型 对象名 = new 子类类型()

Animal animal = new Cat("Mimi",1);

Animal是父类类型,但可以引用一个子类对象,因为是从小范围大范围的转换(子类是小范围,父类是大范围,这就类似隐式类型转换),安全性很好

常见的可以发生向上转型的场景:

  1. 直接赋值
    这个就是刚才上面所举的例子

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

public static void eat(Animal a){a.eat();
}public static void main(String[] args) {Cat cat = new Cat("Mimi",1);eat(cat);
}
  1. 作为返回值返回
    方法的返回类型为父类类型,返回任意子类对象
public Animal func() {Dog dog = new Dog();return dog;
}

向上转型可以提高代码的灵活性、通用性比如说有一个方法的参数类型是Animal类,那它就可以接收Dog类、Cat类、Bird类等实参,而且根据传递的对象的类型,确定要使用哪个对象的具体表现(比如传Dog类就表现出跑的动作,而传Bird类就表现出飞的动作),这就是我们所讲的多态

二、向下转型
将一个子类对象向上转型之后,它不能调用子类特有的属性、方法,但有时候可能需要调用子类特有的方法,所以将父类引用再还原为子类对象即可,即向下转型
简而言之就是:父类强制类型转换之后给子类。从谁转型上来的,就转下去成为谁

但是向下转型不太安全,因为需要进行强制类型转换,如果转换后的类型与向上转型之前子类对象类型不一致的话,运行时就会抛异常
举个例子,比如我想让狗喵喵叫,这肯定是不行的

    public static void main(String[] args) {Animal animal = new Cat();Animal animal1 = new Dog();Dog dog = new Dog();dog = (Dog)animal1;  //向下转型成功dog = (Cat)animal;    //转换失败,因为animal不是由Cat类型向上转型得到的dog.mew();}

为了防止报错,可以在向下转型之前使用关键字instanceof进行检验
o1 instanceof o2
instanceof可以用来判断o1是否是o2或者o2子类实例化的对象,如果是,返回true;反之返回false

而对于第三个条件:通过父类的引用调用重写的方法。其实你会发现只要满足前面两个条件,那第三个条件肯定也满足了
满足三个条件之后,就会发生动态绑定,它是多态的基础。而有动态绑定,那自然也有静态绑定,下面对这两个概念进行解析

🍌静态绑定&动态绑定

  1. 静态绑定

编译阶段就确定要调用哪个函数

    int add(int x,int y) {return x+y;}int add(int x,int y,int z) {return x+y+z;}

比如上面两个add方法构成重载,在编译阶段根据所传参数个数就能确定要调用哪个add方法

  1. 动态绑定
    当一个父类的引用指向一个子类的对象时,可以通过父类的引用调用子类重写的方法。这种情况下,Java会根据对象的实际类型来决定调用哪个方法,这就是动态绑定

动态绑定是在程序运行期间才确定要调用哪个方法

    public static void main(String[] args) {Animal animal = new Dog("圆圆",19);animal.eat();}

在这里插入图片描述
汇编代码观察结果

当调用一个方法时,编译器会根据引用的类型来确定要调用的方法,所以在编译阶段,调用的还是Animal的eat方法(通过汇编可以观察到)
但是程序运行时,实际上被调用的方法是由引用所指向的对象的类型决定的所以我们看到运行结果是调用Dog的eat方法

🍌多态的利弊

多态的好处:

  • 能够简化代码,避免使用大量的 if - else
    比如要写一个类来打印不同的图案(○、△、❀),如果不基于多态,实现的代码如下:
public class Shape {public Shape shape;public void draw() {System.out.println("画一个图形");}
}public class Circle extends Shape{@Overridepublic void draw() {System.out.println("○");}
}public class Triangle extends Shape{@Overridepublic void draw() {System.out.println("△");}
}public class Flower extends Shape{@Overridepublic void draw() {System.out.println("❀");}
}public static void main(String[] args) {String[] array = {"Triangle","Circle","Circle","Flower"};for(String shape:array) {if(shape.equals("Triangle")) {System.out.println("△");} else if(shape.equals("Circle"))  {System.out.println("○");} else if (shape.equals("Flower")) {System.out.println("❀");}}}

如果使用多态,就可以简化为:

public class Shape {public Shape shape;public void draw() {System.out.println("画一个图形");}
}public class Circle extends Shape{@Overridepublic void draw() {System.out.println("○");}
}public class Triangle extends Shape{@Overridepublic void draw() {System.out.println("△");}
}public class Flower extends Shape{@Overridepublic void draw() {System.out.println("❀");}
}public static void main(String[] args) {Shape[] array = {new Triangle(),new Circle(),new Circle(),new Flower()};for(Shape shape:array) {shape.draw();}
}

以子类对象为数组元素,通过for循环调用draw函数打印相应的图案
在这里插入图片描述

  • 可扩展能力更强
    以上面打印图案为例,如果要新增一种新的形状,使用多态的方式代码改动成本也比较低

缺陷:

  • 属性没有多态性
    当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性
  • 构造方法没有多态性
    注意:不要在构造方法里面调用重写的方法!!!因为会发生动态绑定,调用子类重写的方法,但此时子类还没构造完成,可能会出现一些极难被发现的问题

🍉写在最后

以上就是本篇文章的全部内容,如果你觉得本文对你有所帮助的话,那不妨点个小小的赞哦!(比心)

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

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

相关文章

FFmpeg之SWScale

文章目录 一、概述二、函数调用结构图三、Libswscale处理数据流程四、重要结构体4.1、SwsContext4.2、SwsFilter 五、重要函数5.1、sws_getContext5.1.1、sws_alloc_context5.1.2、sws_init_context 5.2、sws_scale5.2.1、SwsContext中的swscale()5.2.2、check_image_pointers5…

全网最详细!!Python 爬虫快速入门

1. 背景 最近在工作中有需要使用到爬虫的地方,需要根据 Gitlab Python 实现一套定时爬取数据的工具,所以借此机会,针对 Python 爬虫方面的知识进行了学习,也算 Python 爬虫入门了。 需要了解的知识点: Python 基础语…

three.js从入门到精通系列教程002 - three.js正交相机OrthographicCamera

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>three.js从入门到精通系列教程002 - three.js正交相机OrthographicCamera</title><script src"ThreeJS/three.js"></script><script src&qu…

免费200万Tokens 用科大讯飞API调用星火大模型服务

简介 自ChatGPT火了之后&#xff0c;国内的大模型发展如雨后春笋。其中的佼佼者之一就是科大讯飞研发的星火大模型,现在大模型已经更新到V3 版本&#xff0c;而且对开发者也是相当友好&#xff0c;注册就送200万tokens,讯飞1tokens 约等于 1.5 个中文汉字 或者 0.8 个英文单词…

[LitCTF 2023] Web类题目分享

[LitCTF 2023] Web类题目做法及思路解析&#xff08;个人分享&#xff09; 题目平台地址&#xff1a;NSSCTF | 在线CTF平台 一、[LitCTF 2023]我Flag呢&#xff1f; 奇怪&#xff0c;放哪里了&#xff0c;怎么看不见呢&#xff1f;&#xff08;初级难度&#xff09; 1.访问…

Zookeeper安装教程

系列文章目录 Zookeeper简介 文章目录 前言一、选择安装包二、使用wget下载并安装zookeeper 前言 Linux下Zookeeper安装步骤 一、选择安装包 Zookeeper下载地址&#xff1a;https://zookeeper.apache.org/releases.html 选择一个稳定版本即可&#xff0c;我这里选择的是3.7.2…

技术分享 | App常见bug解析

在 app 产品测试过程中&#xff0c;可能会遇到很多不同类型的 Bug。知道了可能 Bug 的类型&#xff0c;有利于在测试过程中更好的预防这些问题的发生。 功能Bug 内容显示错误 前端页面展示的内容有误。 这种错误的产生有两种可能 前端代码写的文案错误接口返回值错误 功能…

【分布式技术】监控平台zabbix对接grafana,优化dashboard

目录 第一步&#xff1a;在zabbix server服务端安装grafana&#xff0c;并启动 第二步&#xff1a; 访问http://ip:3000/login 第三步&#xff1a;创建数据源 第四步&#xff1a;导入dashboard模板 ps&#xff1a;自定义创建新面板 第一步&#xff1a;在zabbix server服务…

【LeetCode热题100】【子串】滑动窗口最大值

题目 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例 1&#xff1a; 输入&#xff1a;nums [1,3,-1,-3,5,3,6,7], …

Python守护线程及作用

有一种线程&#xff0c;它是在后台运行的&#xff0c;它的任务是为其他线程提供服务&#xff0c;这种线程被称为“后台线程&#xff08;Daemon Thread&#xff09;”&#xff0c;又称为“守护线程”或“精灵线程”。Python 解释器的垃圾回收线程就是典型的后台线程。 后台线程…

Kafka Console Client 的 Consumer Group

以往使用 kafka-console-consumer.sh 消费 Kafka 消息时并没有太在意过 Consumer Group&#xff0c;在命令行中也不会使用 --group 参数&#xff0c;本文针对 Kafka Console Client 命令行中的 Consumer Group 进行一次统一说明。 1. 如不设置 --group 参数会自动生成一个 Con…

排序:非递归的归并排序

目录 递归与非递归的思想对比&#xff1a; 递归&#xff1a; 非递归&#xff1a; 代码解析&#xff1a; 完整代码&#xff1a; 递归与非递归的思想对比&#xff1a; 递归&#xff1a; 在之前的归并排序&#xff0c;它的核心思想是通过不断的分割&#xff0c;从一个数组变…