java多态性_深入理解java多态性
引言:多态是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。多态性增强了软件的灵活性和扩展性。
多态性:发送消息给某个对象,让该对象自行决定响应何种行为。 通过将子类对象引用赋值给超类对象引用变量来实现动态方法调用。
java 的这种机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法, 但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。
引用在一个论坛看到的一个比喻,很形象;
比方一下:
你和你朋友都是烟鬼,
某日,你没有带烟, 而你朋友有n种烟,
你说: 来根烟,
朋友给你一根;
你通过烟的过滤嘴, 品出为白沙烟
你再要一根,
品出这次是芙蓉王,
再要一根,
品出这次是熊猫
...
上面描述的就是多态!
烟 a = 白沙
烟 b = 芙蓉王
烟 c = 熊猫
但发现没有, 仅仅通过"烟"基类, 引用到不同的具体类对象,
就能让你调用到不同的对象, 这就是多态.
而烟到你嘴里[此时你还没有发现是那种烟], 你仅仅凭借"吸"的动作,就识别出来,到底是哪种烟对象
这就是多态的悬机.
多态须具备:
1. 基类 和各个子类
2. 基类 引用, 指向实例化的子类对象.
再举一个鸡的例子,
鸡是所有其它鸡的基类, 定义了一个方法shape(), 此方法能指明鸡的形态. 所有的子类[火鸡,山鸡,田鸡等],都有这个shape方法, 表明自己的形态,
如果用下面的方法定义我需要具体的鸡,
[基类 引用 = 实现类的对象]
for example:
鸡 a = new 火鸡();
鸡 a = new 田鸡();
鸡 a = new 山鸡();
鸡 a = new 母鸡();
当我使用的时候, 我采用 a 这个引用,
a.shape(), 就会得到鸡的形态.
如果我定义的是
鸡 a = new 火鸡();
那么a.shape得到的是火鸡的形态, 而不需要用具体的
火鸡 a = new 火鸡(); 再通过a.shape得到火鸡的形态
----------------------------------------------------------------------------------
多态分两种:
(1) 编译时多态:编译时动态重载;
(2) 运行时多态:指一个对象可以具有多个类型,方法的覆盖
这样对于对象而言分为:
理解运行时多态:
Car c = new Bus();
Car编译时类型 编译时检查变量类型是否存在,是否有调用的方法。
Bus运行时类型 实际运行是访问heap中的对象,调用实际的方法。
运行时多态是由运行时类型决定的
编译时多态是由编译时类型决定的
Java的多态性总结:
(1) 首先, 要明确Child对Parent发生了方法的overload还是overwrite,根据参数类型及个数。
(2) 如果是overload,则Child继承了Parent的方法,相当于Child多加了父类的方法。
(3) 如果是overwrite, 则Child类不会有父类的该方法,而是把该方法重写了,所以该方法存在于父类中,子类只是重写了。要想调用父类的该方法,只能通过super关键字调用。
java 的这种机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。
(4) 接下来,观察方法执行时的参数类型;
(5) 然后看调用对象的声明类型和实例类型;
(6) 方法的调度采用最精确参数匹配。
引用一个经典多态的例子:来源于:
class A {
public String show(D obj)...{
return ("A and D");
}
public String show(A obj)...{
return ("A and A");
}
}
class B extends A{
public String show(B obj)...{
return ("B and B");
}
public String show(A obj)...{
return ("B and A");
}
}
class C extends B...{}
class D extends B...{}
(二)问题:以下输出结果是什么?
A a1 = new A();
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println(a1.show(b)); ①
System.out.println(a1.show(c)); ②
System.out.println(a1.show(d)); ③
System.out.println(a2.show(b)); ④
System.out.println(a2.show(c)); ⑤
System.out.println(a2.show(d)); ⑥
System.out.println(b.show(b)); ⑦
System.out.println(b.show(c)); ⑧
System.out.println(b.show(d)); ⑨
(三)答案
① A and A
② A and A
③ A and D
④ B and A
⑤ B and A
⑥ A and D
⑦ B and B
⑧ B and B
⑨ A and D
(四)分析
①②③比较好理解,一般不会出错。④⑤就有点糊涂了,为什么输出的不是"B and B”呢?!!先来回顾一下多态性。
运行时多态性是面向对象程序设计代码重用的一个最强大机制,动态性的概念也可以被说成“一个接口,多个方法”。Java实现运行时多态性的基础是动态方法调度,它是一种在运行时而不是在编译期调用重载方法的机制。
方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现,重载Overloading是一个类中多态性的一种表现。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写(Overriding)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被“屏蔽”了。如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现,重载Overloading是一个类中多态性的一种表现。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被“屏蔽”了。如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。
当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。 (但是如果强制把超类转换成子类的话,就可以调用子类中新添加而超类没有的方法了。)
好了,先温习到这里,言归正传!实际上这里涉及方法调用的优先问题 ,优先级由高到低依次为:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。让我们来看看它是怎么工作的。
比如④,a2.show(b),a2是一个引用变量,类型为A,则this为a2,b是B的一个实例,于是它到类A里面找show(B obj)方法,没有找到,于是到A的super(超类)找,而A没有超类,因此转到第三优先级this.show((super)O),this仍然是a2,这里O为B,(super)O即(super)B即A,因此它到类A里面找show(A obj)的方法,类A有这个方法,但是由于a2引用的是类B的一个对象,B覆盖了A的show(A obj)方法,因此最终锁定到类B的show(A obj),输出为"B and A”。
再比如⑧,b.show(c),b是一个引用变量,类型为B,则this为b,c是C的一个实例,于是它到类B找show(C obj)方法,没有找到,转而到B的超类A里面找,A里面也没有,因此也转到第三优先级this.show((super)O),this为b,O为C,(super)O即(super)C即B,因此它到B里面找show(B obj)方法,找到了,由于b引用的是类B的一个对象,因此直接锁定到类B的show(B obj),输出为"B and B”。
按照上面的方法,可以正确得到其他的结果。
问题还要继续,现在我们再来看上面的分析过程是怎么体现出蓝色字体那句话的内涵的。它说:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。还是拿a2.show(b)来说吧。
java多态性_深入理解java多态性相关推荐
- java 枚举_深入理解Java枚举
所有知识体系文章,[GitHub](https://github.com/Ziphtracks/JavaLearningmanual)已收录,欢迎Star!再次感谢,愿你早日进入大厂! https:/ ...
- java 注解_怎样理解 Java 注解和运用注解编程?
怎样理解 Java 注解和运用注解编程? 注解和使用 先来看下概念首先从注释来看: 注释:给代码添加说明和解释,注释帮助开发人员理解程序.(Comment)说白点就是注释是给人看的. 注解:给代码添加 ...
- 如何理解java反射_怎么理解java反射
怎么理解java反射? 概述 Java 反射是可以让我们在运行时获取类的方法.属性.父类.接口等类的内部信息的机制.也就是说,反射本质上是一个"反着来"的过程.我们通过new创建一 ...
- 深入理解java虚拟机_深入理解Java类加载
本文目的: 深入理解Java类加载机制; 理解各个类加载器特别是线程上下文加载器; Java虚拟机类加载机制 虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验.转换解析和初始化,最 ...
- Java继承_深入理解Java继承、封装、多态的实现原理
点击关注上方"Java技术江湖",设为"置顶或星标",第一时间送达技术干货. 作者:黄小斜 文章来源:微信公众号[Java技术江湖] 目录 从JVM结构开始谈多 ...
- kindle版java核心_深入理解java 7 核心技术与最佳实践-成富[6寸pdf mobi epub kindle版].pdf...
深入理解java 7 核心技术与最佳实践-成富[6寸pdf mobi epub kindle版].pdf 还剩 50页未读, 继续阅读 下载文档到电脑,马上远离加班熬夜! 亲,很抱歉,此页已超出免费预 ...
- java 部分正确性_深入理解java快速排序的正确性
说起快速排序,很多人都能够写出一个正确的快速排序,但就快速排序的正确性,就无从探究了.为什么说写出来的快速排序就是正确的.在快速排序中间的关键几步,以什么样的数据组织来保障快速排序的正确性.本文以&l ...
- vilatile 深入理解java虚拟机_深入理解Java虚拟机(jvm性能调优+内存模型+虚拟机原理)...
本套课程从虚拟机的发展历史,虚拟机的内存结构,对象的分配与回收以及字节码,类加载等多个方面深入地对Java虚拟机进行了剖析.内容详实,语言通俗易懂.理论结合实践,让学习本套视频的朋友可以更快的理解虚拟 ...
- 自定义JAVA注解_深入理解Java:自定义java注解
要深入学习注解,我们就必须能定义自己的注解,并使用注解,在定义自己的注解之前,我们就必须要了解Java为我们提供的元注解和相关定义注解的语法. 元注解: 元注解的作用就是负责注解其他注解.Java5. ...
最新文章
- mysql 执行顺序 别名_sql语句的执行顺序和别名问题
- 安卓学习第17课——Gallery
- 点击事件为什么会失效_1917年的法蒂玛事件,为什么会被称为最为惊悚的UFO目击事件?...
- c# bool?和bool_C#中的bool关键字
- NGRAIN在模型轻量化上的应用
- 【code vs】 2780 ZZWYYQWZHZ
- 2021-09-08因子分解机
- 跟着翁凯老师学Cday1#学习记录#
- FreeSSL.cn 申请免费HTTPS 证书
- 数智融合加速驱动企业商业创新
- 解决在浏览器中打开java文件jnlp报错“该文件没有程序与之关联来执行该操作...”
- Vue.js 最新版官方下载地址
- python建立窗口并美化_Python GUI教程(十六):在PyQt5中美化和装扮图形界面
- Processing——码绘与手绘对比动态篇
- HackTheBox –Craft实战
- cmd-命令行中打开新cmd窗口
- 足球比分动态 API数据接口
- 彩超探头频率高低的区别_【干货】超声探头及使用技巧
- 非正式的沟通往往比正式的沟通更有效,更重要
- 《FLUENT 14.0超级学习手册》——1.3 CFD软件的构成