java方法的多态性理解
1.什么是java的多态
浏览了别人博客中的一些介绍多态的文章,发现大家的描述有点不一样,主要区别在于是否把方法的重写算做多态。一种我比较认同的说法如下:
多态分为两种
a. 编译时多态:方法的重载;
b. 运行时多态:JAVA运行时系统根据调用该方法的实例的类型来决定选择调用哪个方法则被称为运行时多态。(我们平时说得多的事运行时多态,所以多态主要也是指运行时多态);
上述描述认为重载也是多态的一种表现,不过多态主要指运行时多态。
2.运行时多态
a. 面向对象的三大特性:封装、继承、多态。从一定角度来看,封装和继承几乎都是为多态而准备的。这是我们最后一个概念,也是最重要的知识点。
b. 多态的定义:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)
c. 实现多态的技术称为:动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
d. 多态的作用:消除类型之间的耦合关系。
e. 现实中,关于多态的例子不胜枚举。比方说按下 F1 键这个动作,如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;如果当前在 Word 下弹出的就是 Word 帮助;在 Windows 下弹出的就是 Windows 帮助和支持。同一个事件发生在不同的对象上会产生不同的结果。
下面是多态存在的三个必要条件,要求大家做梦时都能背出来!
多态存在的三个必要条件
一、要有继承;
二、要有重写;
三、父类引用指向子类对象。
多态的好处:
1.可替换性(substitutability)。多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。
2.可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。
3.接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。如图8.3 所示。图中超类Shape规定了两个实现多态的接口方法,computeArea()以及computeVolume()。子类,如Circle和Sphere为了实现多态,完善或者覆盖这两个接口方法。
4.灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。
5.简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。
3.代码理解
看了上面的描述,我们大概知道以下几点:
a. 运行时多态是在父类引用指向子类对象时产生的。一个父类的引用可以指向多种子类对象,那么运行时对于同一个消息应该如何做出响应呢?这就由实际的被引用的对象的类型来决定。
b. 为什么要有重写呢?难道父类中的方法没有被重写,直接调用子类中存在的方法难道是不行吗?看个例子如下:
上面的例子中,当父类中的getName()被注释掉以后,调用father.getName()方法会出错。因此,当父类引用指向子类方法时,必须调用那些父类中存在的方法,如果子类中对该方法进行了重写,那么在运行时就会动态调用子类中的方法,这就是多态。
c. 要有继承很好理解,没有继承的话,哪来的重写呢。
4.深一点
基本了解了多态以后,我们就可以看明白下面这个例子了,它的输出结果是什么呢?
答案是"son",结合前面的解释,我们很容易判断出来。子类的getName()方法重写了父类中的方法,在main中,父类的引用father指向了子类的对象son,当我们调用father的getName()方法时,实际上是动态绑定到了子类的getName()上,所以会打印出"son"。
5.再深一点
你是否真正理解了多态呢?请看下面的例子:
上面这个例子中,下面四条语句的输出结果是什么呢?
a1.show(b);
a1.show(c);
a2.show(b);
a2.show(c);
结果如下:
对于前两条语句的结果我们很容易理解,那第三条和第四条的,为什么结果和我们想的不一样,不应该是"B and B"吗?要理解这是为什么,我们要先理解下面这句话:
当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。 (但是如果强制把超类转换成子类的话,就可以调用子类中新添加而超类没有的方法了)
看一下标红的那句话,我们知道问题所在了吗?
当运行 a2.show(b) 的时候,实际是要调用一个 show(B obj) 的方法,但是 A 中有这样一个方法吗?没有!但是由于 B 继承自 A,所以会调用 A 中的 show(A obj) 的方法,但是调用时候发现这个方法已经被 B 重写了,所以就会转向来调用 B 中的 show(A obj) 方法。所以才会打印出"B and A"。
实际上这里涉及方法调用的优先问题 ,优先级由高到低依次为: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”。
怎么样?理解了吗?
问题还要继续,现在我们再来看上面的分析过程是怎么体现出红色字体那句话的内涵的。它说:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。还是拿a2.show(b)来说吧。
a2是一个引用变量,类型为A,它引用的是B的一个对象,因此这句话的意思是由B来决定调用的是哪个方法。因此应该调用B的show(B obj)从而输出"B and B”才对。但是为什么跟前面的分析得到的结果不相符呢?!问题在于我们不要忽略了蓝色字体的后半部分,那里特别指明:这个被调用的方法必须是在超类中定义过的,也就是被子类覆盖的方法。B里面的show(B obj)在超类A中有定义吗?没有!那就更谈不上被覆盖了。实际上这句话隐藏了一条信息:它仍然是按照方法调用的优先级来确定的。它在类A中找到了show(A obj),如果子类B没有覆盖show(A obj)方法,那么它就调用A的show(A obj)(由于B继承A,虽然没有覆盖这个方法,但从超类A那里继承了这个方法,从某种意义上说,还是由B确定调用的方法,只是方法是在A中实现而已);现在子类B覆盖了show(A obj),因此它最终锁定到B的show(A obj)。这就是那句话的意义所在,到这里,我们可以清晰的理解Java的多态性了。
6. 最后一个练习!
看下面的例子:
上面例子中的输出是什么呢?答案是:ai ni
有了前一个例子我们就会很容易理解这个例子。在B类中是没有对A中的show方法进行重写,所以当a.show()时调用的是父类中的show方法,父类中show方法调用了show2方法,但是在调用的时候发现show2方法已经被子类重写,因此会调用子类中show2方法,因此输出"ai"。可见,当父类引用指向子类对象的时候,对父类中方法的调用都会绑定到子类中重写后的方法上,如果子类没有对方法进行重写,那么会直接调用父类中的方法,相当于是直接调用从父类继承的方法。
还需要注意的一点是:子类在重写父类的方法时,方法的访问权限不能更低!
java方法的多态性理解的更多相关文章
- Java方法区的理解
方法区逻辑上是属于堆的一部分,但一些简单的实现可能不会选择去进行垃圾收集或者进行压缩. 但对于HotSpotJVM而言,方法区还有一个别名叫做Non-Heap,目的就是要和堆分开 所以方法区看作是一块 ...
- 这篇建议java开发都看看,对Java方法及加深理解的很深,值得一看!
方法和加深 方法的定义 修饰符 返回类型 break:跳出switch,结束循环 和 return 的区别 方法名:注意规范 见名知意 参数列表(参数类型,参数名)- 异常抛出 // Demo01 类 ...
- 理解Java方法增强
在实际开发中,我们往往需要对某些方法进行增强,常用的方法增强的方式有三种. 类继承 .方法覆盖 必须控制对象创建,才能使用该方式 装饰者模式方法加强 必须和目标对象实现相同接口或继续相同父类,特殊构造 ...
- JAVA面向对象-多态的理解
面向对象编程有三个特征,即封装.继承和多态. 封装隐藏了类的内部实现机制,从而可以在不影响使用者的前提下改变类的内部结构,同时保护了数据. 继承是为了重用父类代码,同时为实现多态性作准备.那么什么是多 ...
- 将子类对象引用赋值给超类对象 JAVA 编译时多态性
将子类对象引用赋值给超类对象 JAVA 编译时多态性(转) (2012-05-10 11:24:05) 转载▼ 标签: 杂谈 分类: 也无晴_soft 1.通过将子类对象引用赋值给超类对象引用变量来实 ...
- Java中hashcode的理解
Java中hashcode的理解 原文链接http://blog.csdn.net/chinayuan/article/details/3345559 怎样理解hashCode的作用: 以 java. ...
- 通过字节码分析Java方法的静态分派与动态分派机制
在上一次[https://www.cnblogs.com/webor2006/p/9723289.html]中已经对Java方法的静态分派在字节码中的表现了,也就是方法重载其实是一种静态分派的体现,这 ...
- Java方法调用机制
最近在编程时,修改方法传入对象的对象引用,并没有将修改反映到调用方法中.奇怪为什么结果没有变化,原因是遗忘了Java对象引用和内存分配机制.本文介绍3个点: ① 该问题举例说明 ② 简要阐述Java内 ...
- Java:面向对象的理解
面向对象 一切皆对象.程序是对象的集合,它们通过发送消息来告知彼此所要做的.也就是说:以对象为中心,以消息(发送消息即为函数调用)为驱动.对象具有状态,行为和标识. 状态:指类的数据成员,即属性: 行 ...
随机推荐
- 在GNU/Linux下使用Lilypond排版简谱
尽管GNU/Linux并非无所不能,但确实能在很多时候提供免费.开放的解决方案.这两天我想做一个简谱,在网上搜索乐谱排版软件,发现了基于GPL协议的Lilypond软件.只不过Lilypond是用来做 ...
- [转]Numpy使用MKL库提升计算性能
from:http://unifius.wordpress.com.cn/archives/5 系统:Gentoo Linux (64bit, Kernel 3.7.1)配置:Intel(R) Cor ...
- QQSpamerUpdate
2.00001,0,http://oia5k1lqi.bkt.clouddn.com/QQSpamer2.0%20BETA1.exe
- 使用canvas实现超绚丽的旋转正方形
自己无意中的一个小"bug",却让动画变得超绚丽= = 所以,不要害怕出bug,谁知道bug不会开出一朵绚丽的花呢? <!DOCTYPE html> <html ...
- hdu1033
#include<stdio.h> #include<string.h> const int MAXN=200; char str[MAXN]; int main() { in ...
- Python学习笔记——基础篇【第二周】——解释器、字符串、列表、字典、主文件判断、对象
目录 1.Python介绍 2.Python编码 3.接受执行传参 4.基本数据类型常用方法 5.Python主文件判断 6.一切事物都是对象 7. int内部功能介绍 8.float和long内 ...
- 小例子解释wait与notify的区别
系统慢可能有很多种原因,硬件资源不足,语句不优化,结构设计不合理,缺少必要的运维方式.所有的这些问题都可以在阻塞与等待中看出端倪,发现并解决问题. 首先是下载开发工具,磨刀不误砍材工.点此下载 这是一 ...
- github本地库及clone常用命令
新建本地库 1. cd d: 2. mkdir git 3. cd git 4. git init 5. git remote add origin git@github.com:swportal/ ...
- Java语法基础总结
这次上课我们讲了Java语言中的枚举类型,并进行了一次课堂测试,测试代码如下: public class EnumTest { public static void main(String[] arg ...
- SQLServer 重建索引前后对比
在做维护项目的时,我们经常会遇到索引维护的问题,通过语句,我们就可以判断某个表的索引是否需要重建. 执行一下语句:先分析表的索引 分析表的索引建立情况:DBCC showcontig('Table') ...