【JavaSE】面向对象---多态

前言

  1. 本篇以Java初学者视角写下,难免有不足,或者术语不严谨之处。如有错误,欢迎评论区指正。
  2. 本篇说明多态相关的知识。
  3. 若本文无法解决您的问题,可以去最下方的参考文献出,找出想要的答案。

在这里插入图片描述

多态概念

多态(polymorphism):多种形态,具体是不同对象完成某个行为,它们的状态不同。
比如,同样是跑1000米,有些人气喘吁吁(我),还有一些大佬跑完气都不带喘的。
我认为多态是一种思想。而要理解多态得熟悉向上转型,和方法重写的知识。
有关笔者继承的博客:可以浅读了解一下

向上转型

字面意思:转型,类型转换;向上,即子类向超类转换。
向上转型属于自动转换类型可以不用强制类型转换运算符。

public class Main {public static void main(String[] args) {Dog dog =Dog.Init("旺财",2);//这里采用的是静态工厂方法,不熟悉的可以看下面的参考文献阅读一下。Animal animal = dog;//这里Dog类转换成了父类Animal}
}

不熟悉的可以先跳过,这里只是为了代码的完整性放这儿/

//Dog.java
class Dog extends Animal{public static Dog Init(String name,int age){Dog dog = new Dog(name,age);return dog;}public Dog(String name,int age){super(name,age);}public Dog(){}@Overridepublic void eat() {System.out.println(this.name+" 正在吃狗粮.......");}
}
//Animal.java
class Animal {String name;int age;public Animal(String name,int age){this.name=name;this.age =age;}public Animal(){}public static void fun(Animal animal){animal.eat();}public void eat(){System.out.println(this.name + "正在吃法。");}}

在Java中,对象变量是多态的,它既可以引用自身的类型,还可以引用其子类的对象。

实现向上转型有三种方式
1.直接赋值

 Animal animal = new Dog();//直接创建一个子类对象然后赋值给超类的对象变量

2.方法传参

//Main.java
public class Main {public static void main(String[] args) {Animal.fun1(new Dog());}
}//Animal.java类中有如下静态方法
public static void fun1(Animal animal){}

这种通过调用超类的方法传子类的实参来达到向上转型的效果。

3.返回值

//Animal.java中有如下静态方法。
//函数内部实例化子类对象,然后由于返回值要与类型匹配,自动地发生向上转型。
public static Animal fun2(){Dog dog = new Dog();return dog;}

动态绑定

这里说明动态绑定,但涉及到方法重写(先忽略这个问题)。
以下是三个类,两个子类一个超类,重点关注每个类中的eat方法.

//Dog.java
class Dog extends Animal{public static Dog Init(String name,int age){Dog dog = new Dog(name,age);return dog;}public Dog(String name,int age){super(name,age);}//关注eat方法@Overridepublic void eat() {System.out.println(this.name+" 正在吃狗粮.......");}
}
//Animal.java
class Animal {String name;int age;public Animal(String name,int age){this.name=name;this.age =age;}public static void fun(Animal animal){animal.eat();}//关注eat方法public void eat(){System.out.println(this.name + "正在吃饭。");}}
//Bird.java
class Bird extends Animal{public static Bird Init(String name,int age){Bird bird = new Bird(name,age) ;return bird;}public Bird(String name,int age){super(name,age);}//关注eat方法@Override//eren jegerpublic void eat() {System.out.println(this.name+" 正在吃虫子.......");}
}

🆗接下来来分析Main类了

public class Main {public static void main(String[] args) {Animal animal= Dog.Init("kunkun",1);animal.eat();//这里的eat调用的是哪个类里面eat方法?}
}

在这里插入图片描述

调用的是子类Dog,为什么呢?这里不是向上转型成Animal(超类)了吗?
嗯,这里就得介绍本标题的主角:动态绑定机制

  1. 动态绑定机制,简单来说,以父类为编译类型,创建子类对象的时候,绑定子类运行类型,当我们再调用方法的时候,能够有序的寻找方法,实现方法的有序调用。
  2. 解释:1.动态绑定机制在运行时调用方法绑定子类运行。
    2.程序在编译时,确实调用的时父类的eat方法。当运行代码的时候,通过父类的引用,调用了父类和子类重写的方法,结果实际调用了子类的方法,此时称这种情况为动态绑定。
    3.程序运行并且采用动态绑定调用方法时,JVM会调用父类所引用对象的实际类型的方法。

    三种解释任选理解。下面来说明方法重写。

方法重写

在上面的例子中用到方法重写,我们这里详细说说。

//Dog类中(子类)public void eat() {System.out.println(this.name+" 正在吃狗粮.......");}
//Bird类中(子类)public void eat() {System.out.println(this.name+" 正在吃虫子.......");}
//Animal类中(超类)public void eat(){System.out.println(this.name + "正在吃饭。");}

显而易见,Animal执行eat打印的是某某正在吃饭,而Dog打印的是某某吃狗粮。
超类的有些方法对子类并不适用,这里就是不够具体。每种动物的食物不同,这里就可以通过方法重写来针对特定的类写对应的eat方法来覆盖原先超类的方法。
没错,这就是方法重写(又称覆盖方法)。

若子类和超类的方法满足以下:
1.方法名相同
2.参数相同(参数类型,参数个数,参数序列相同)
3.返回类型相同
则构成方法重写。

方法重写的注意事项:

1.若方法前面有private static final 修饰,或者其本身是构造器(构造方法),编译器本身可以确定应该调用哪些方法。这些方法不能重写。
2.方法重写时,子类的访问权限一定大于等于超类的访问权限。若超类是public,则子类也必须是public类,不能protected等等。
3.子类和超类的覆盖方法返回类型可以不同,但必须构成父子关系。
`

//Animal类一部分public Animal eat(){System.out.println(this.name + "正在吃饭。");return null;}//Dog类一部分public Dog eat() {System.out.println(this.name+" 正在吃狗粮.......");return null;}
}

重写与重载区别

重写与重载都是一种多态。
方法重载是类之间的多态。方法重写是子类与超类的多态。

方法重载:
方法之间同名,但参数列表必须修改。
返回类型,访问修饰符随意。

方法重写:
1.方法名必须相同
2.参数相同(参数类型,参数个数,参数序列相同)
3.返回类型相同(构成父子关系可以不同)
4.访问修饰符必须满足子类权限大于等于超类。

下面是补充部分:

补充

向下转型

向下转型有风险。向下转型是超类强制转换成子类。这里必须借助强制转换运算符。
系统来说:将一个超类类型的引用变量强制转换成子类的过程叫做向下转型。
但不是所有对象都能向下转型,只要当这个对象原本就是子类向上转型过来时才能向下转型。
???啥意思呢?
举例

public class Main {public static void main(String[] args) {Animal animal= new Animal("kunkun",1);//直接new一个超类对象向下转型Dog dog =(Dog)animal;dog.eat();//结果如何呢?打印Dog的eat还是Animal的eat呢?}
}

结果是都不打印,直接报错。
在这里插入图片描述

//本来是狗,还原成狗,可以。
public class Main {public static void main(String[] args) {Animal animal= Dog.Init("kunkun",2);Dog dog =(Dog)animal;dog.eat();}
}```java
//本来是狗,还原成鸟不合理。你家狗会飞???
public class Main {public static void main(String[] args) {Animal animal= Dog.Init("kunkun",2);Bird bird =(Bird)animal;bird.fly();}
}

在这里插入图片描述


> 总结:
> 向下转型唯一正确的使用用法:
> 1.先向上转型,让父类引用子类对象。
> 2.向下转型类型要与最初子类类型相同。别从一个子类转换成另一个子类了。报错警告!##  instanceof关键字
**instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。**
[instanceof关键字详解](http://t.csdnimg.cn/IIZbD)
```java
public class Main {public static void main(String[] args) {Animal animal= Bird.Init("kunkun",2);Bird bird =(Bird)animal;if( bird instanceof Bird){bird.fly();}else{System.out.println(false);}}
}

静态绑定

其实前面提过:若方法前面有private static final 修饰,构造器(构造方法),编译器本身可以确定在那个类调用这些方法。
像上述在这种程序执行前就已经绑定了方法在那个类中调用的方法称为静态绑定。

避免在构造方法中使用重写

//Test.java
class B {
public B() {
// do nothingfunc();
}
public void func() {System.out.println("B.func()");
}
}
class D extends B {private int num = 1;public void func() {System.out.println("D.func() " + num);}
}
public class Test {public static void main(String[] args) {D d = new D();
}
}

看main方法,首先实例化D类对象,先进入D中构造方法,构造方法为默认构造方法,由于继承关系,子类构造还有里面还要调用父类构造方法。父类构造方法里的调用func()即子类和父类方法重写,由于动态绑定,会调用子类的func(),而此时子类的num还没初始化为1,默认为0.所以最终打印结果为 D.func() 0
在这里插入图片描述

总之,构造器中最后不要调用实例化的方法,调用静态绑定的方法比如final,private修饰的方法。因为,若实例方法被子类重写,那么就动态绑定了,会调用子类的方法。

参考文献

  • Instance关键字详解
  • 静态绑定与动态绑定
  • 重载与重写—强烈建议看此篇
  • 向上转型与向下转型
  • 类加载顺序
    愿此行,终抵群星。

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

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

相关文章

【小白专用】C# Task 类异步操作-浅谈

注解 Task类表示不返回值并且通常以异步方式执行的单个操作。 Task 对象是在 .NET Framework 4 中首次引入的 基于任务的异步模式 的中心组件之一。 由于对象执行的工作 Task 通常在线程池线程上异步执行,而不是在主应用程序线程上同步执行,因此可以使用…

【上篇】从 YOLOv1 到 YOLOv8 的 YOLO 物体检测模型历史

YOLO 型号之所以闻名遐迩,主要有两个原因:其速度和准确性令人印象深刻,而且能够快速、可靠地检测图像中的物体。 在本文中,我将与大家分享我在阅读一篇长达 30 页的综合性论文时获得的见解,该论文深入探讨了 YOLO 模型的进步。 这篇评论全面概述了 YOLO 框架的演变过程,…

Python001

Python 是一种高级编程语言。它具有以下显著特点:1. 简单易学:语法相对简洁明了,对初学者很友好。2. 丰富的库:拥有大量强大的内置库和第三方库,可用于各种领域,如数据分析、机器学习、Web 开发等。3. 可读…

【无标题】 Notepad++ plugin JSONViewer 下载地址32位

JSONViewer download | SourceForge.net 1、下载插件压缩包并解压出dll:Jsonviewer2.dll(64位)或NPPJSONViewer.dll(32位); 2.、拷贝对应dll到Notepad安装目录下的plugins目录。 3、重启Notepad程序,在插…

LeetCode刷题之HOT100之颜色分类

下午好呀,大家!昨天估计是喝了假酒,现在没有胃口,喝酒真的没有任何好处。以后尽量避免此活动。今天几乎没睡觉,准备做完这题回宿舍,把电脑也带回去。 1、题目描述 2、逻辑分析 对颜色排序,要求…

读书笔记:左耳听风

程序员如何用技术变现 我完全没有必要通过打工听人安排而活着,而是反过来通过在公司工作提高自己的技能,让自己可以更为独立和自由地生活。 因而,在工作当中,对于那些没什么技术含量的工作,我基本上就像是在学生时代那…

NRF24L01(2.4G)模块的使用——SPI时序(软件)篇

一、SPI的简介: SPI 是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。 SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚…

安装ps提示vcruntime140.dll丢失的解决方法,总结5种解决方法

在电脑使用过程中,我们经常会遇到一些错误提示,其中之一就是“找不到vcruntime140.dll”。这个问题可能会导致程序无法正常启动或运行,因此了解其原因和解决方法是非常必要的。小编将从多个方面对“找不到vcruntime140.dll”这一问题进行详细…

iBeacon赋能AR导航:室内定位技术的原理与优势

室内定位导航对于大型商场、机场、医院等复杂室内环境至关重要,它帮助人们快速找到目的地,提高空间利用率。AR技术通过将虚拟信息叠加在现实世界,提供直观导航指引,正在成为室内导航的新趋势,增强用户互动体验&#xf…

【算法训练记录——Day27】

Day27——回溯算法Ⅲ 1.组合总和2.组合总和II3.分割回文串 内容 ● 39.组合总和 ● 40.组合总和II ● 131.分割回文串 1.组合总和 思路&#xff1a;和组合总和一样&#xff0c;先从candidates中遍历选择元素&#xff0c;但是纵向递归时所选择元素要包括当前元素 vector<int&…

玩游戏时服务器遭到DDOS攻击崩溃,DDOS攻击是什么?

某网游服务器突然崩溃&#xff0c;大量玩家被迫下线。随后该游戏官方出面解释是因服务器遭遇了DDoS攻击&#xff0c;所以导致登录异常。这个DDoS攻击到底是哪里来的鬼怪&#xff0c;敢这么给自己加戏&#xff1f; 什么是DDoS攻击&#xff1f; DDoS攻击&#xff0c;专业表述是“…

【启程Golang之旅】协程和管道操作

欢迎来到Golang的世界&#xff01;在当今快节奏的软件开发领域&#xff0c;选择一种高效、简洁的编程语言至关重要。而在这方面&#xff0c;Golang&#xff08;又称Go&#xff09;无疑是一个备受瞩目的选择。在本文中&#xff0c;带领您探索Golang的世界&#xff0c;一步步地了…

积累常用css

1、封面文字&#xff0c;垂直居中&#xff0c;可以两列并排 font-size: 20px;font-weight: 600;color: #333;line-height: 20px;display: block;word-wrap: break-word;writing-mode: vertical-lr;height: 160px;margin: 0 auto; 2、宽border效果 .dashed-box { margin: 80px…

ubuntu22.04编译OpenCV4.9(带contrib-4.9.0)

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;4.9.0 opencv_contrib版本&#xff1a;4.9.0 源码下载 OPenCV4.9.0下载地址&#xff1a;https://github.com/opencv/opencv/releases/tag/4.9.0 如下图所示&#xff1a; 按箭头所指点击下载source code(tar.gz)文件到…

从零开始实现自己的串口调试助手(9)-重置多文本框,保存/载入指令集

重置多文本框 添加多文本控件列表 在构造函数中把我们需要操作的控件归类到对应列表之中 //创建多文本存放数组 - 存放那三列内容 checkBox lineEdi btnfor(int i1;i<9;i){// 添加到按钮数组QString btnName QString("pushButton_t%1").arg(i); //构建对应控件名…

DSP问题:TMS320F280049延时和实际不符

1、问题现象 我之前写的一篇点灯文章&#xff0c;发现LED等闪烁频率和设想不一致&#xff0c;延时100ms&#xff0c;实际延时要更长。 2、问题原因 电路中使用的晶振是10MHz&#xff0c;实际代码中配置的是20MHz的晶振。 3、解决方案 修改代码中的晶振配置为10MHz即可。…

【Redis】Redis经典问题:缓存穿透、缓存击穿、缓存雪崩

目录 缓存的处理流程缓存穿透解释产生原因解决方案1.针对不存在的数据也进行缓存2.设置合适的缓存过期时间3. 对缓存访问进行限流和降级4. 接口层增加校验5. 布隆过滤器原理优点缺点关于扩容其他使用场景SpringBoot 整合 布隆过滤器 缓存击穿产生原因解决方案1.设置热点数据永不…

【C++】C++ 基于QT实现散列表学生管理系统(源码+数据+课程论文)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

力扣hot100:394. 字符串解码(递归)

LeetCode&#xff1a;394. 字符串解码 本题容易想到用递归处理&#xff0c;在写递归时主要是需要明确自己的递归函数的定义。 不过我们也可以利用括号匹配的方式使用栈进行处理。 1、递归 定义递归函数string GetString(string & s,int & i); 表示处理处理整个numbe…