Java动态绑定与多态
在面向对象的程序设计语言中,多态是继数据抽象和继承之后的第三种基本特性。多态通过分离做什么和怎么做,从另一个角度将接口和实现分离开来。在一开始接触多态这个词的时候,我们或许会因为这个词本身而感到困惑,如果我们把多态改称作“动态绑定”,相信很多人就能理解他的深层含义。通常的,我们把动态绑定也叫做后期绑定,运行时绑定。
(一)方法调用绑定
1.绑定概念
通常,我们将一个方法调用同一个方法主体关联起来称作绑定。如果在程序执行前进行绑定,我们将这种绑定方法称作前期绑定。在面向过程语言中,比如c,这种方法是默认的也是唯一的。如果我们在java中采用前期绑定,很有可能编译器会因为在这庞大的继承实现体系中去绑定哪个方法而感到迷惑。解决的办法就是动态绑定,这种后期绑定的方法,在运行的时候根据对象的类型进行绑定。
在java中,动态绑定是默认的行为。但是在类中,普通的方法会采用这种动态绑定的方法,也有一些情况并不会自然的发生动态绑定。
2.final修饰
如果一个属性被final修饰,则含义是:在初始化之后不能被更改。
如果一个方法被final修饰,含义则是不能被覆盖。我们常常喜欢从宏观的角度这样说,但是我们真正的被final修饰的方法为什么不能被覆盖呢?因为final修饰词其实实际上关闭了动态绑定。在java中被final修饰的内容不能采用动态绑定的方法,不能动态绑定就没有多态的概念,自然也就不能被覆盖。
3.“覆盖”私有方法
其实我们很少把方法设定为私有。如果我们将private方法“覆盖”掉,其实我们获得的只是一个新的方法。完全和父类没关系了。这一点要注意,或许面试的时候会被问到:在子类中“覆盖”父类私有方法是被允许而不报错的,只不过完全是两个没关系的方法罢了。
4.域与静态方法
当我们了解了多态性之后可能会认为所有的事物都是可以多态地发生。其实并不是,如果我们直接访问某个域,这个访问会在编译期进行解析,我们可以参考下面的例子:
package Polymorphic;
/**
*
* @author QuinnNorris
* 域不具有多态性
*/
public class polymorphics {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Super sup = new Sub();
System.out.println("sup.field = " + sup.field + ", sup.getField() = "
+ sup.getField());
Sub sub = new Sub();
System.out.println("sub.field = " + sub.field + ", sub.getField() = "
+ sub.getField() + ", sub.getSuperField() = "
+ sub.getSuperField());
}
}
class Super {
public int field = 0;
public int getField() {
return field;
}
}
class Sub extends Super {
public int field = 1;
public int getField() {
return field;
}
public int getSuperField() {
return super.field;
}
}
输出结果:
sup.field = 0, sup.getField() = 1
sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0
这个例子告诉我们,当我们调用一个方法时,去选择执行哪个方法的主体是运行时动态选择的。但是当我们直接访问实例域的时候,编译器直接按照这个对象所表示的类型来访问。于此情况完全相同的还有静态方法。所以我们可以做出这种总结:
- 普通方法:根据对象实体的类型动态绑定
- 域和静态方法:根据对象所表现的类型前期绑定
通俗地讲,普通的方法我们看new后面的是什么类型;域和静态方法我们看=前面声明的是什么类型。
尽管这看来好像是一个非常容易让人混悬哦的问题。但是在实践中,实际上从来(或者说很少)不会发生。首先,那些不把实例域设置为private的程序员基本上已经全都被炒鱿鱼了(实例域很少被修饰成public)。其次我们很少会将自己在子类中创建的域设置成和父类一样的名字。
(二)构造器与多态
通常,构造器是一个很独特的存在。涉及到多态的时候也是如此。尽管构造器并不具有多态性(实际上他们是有static来修饰的,尽管该static是被隐式声明的),但是我们还是有必要理解一下构造器的工作原理。
1.构造器的调用顺序
父类的构造器总是在子类构造器调用的过程中被调用,而且按照继承层次逐渐向上的链接,以使每个父类的构造器都能被正确的调用。这样做是很有必要的,因为构造器有一项特殊的任务,检查对象是否被正确的构造。子类方法只能访问自己的成员,不能访问父类中的成员。只有基类的构造器才具有恰当的权限对自己的元素进行初始化。因此必须要让每个构造器都能得到调用,否则不能构造出正确的完整的对象。
package Polymorphic;
public class Father {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
new F();
}
}
class A {
A() {
System.out.println("A");
}
}
class B extends A {
B() {
System.out.println("B");
}
}
class C extends B {
C() {
System.out.println("C");
}
}
class D {
D() {
System.out.println("D");
}
}
class E {
E() {
System.out.println("E");
}
}
class F extends C {
private D d = new D();
private E e = new E();
F() {
System.out.println("F");
}
}
输出结果:
A
B
C
D
E
F
看似偶然的“ABCDEF”的输出结果,实际上是我们精心安排的。
这个例子非常直观的说明了构造器的调用法则,有以下三个步骤:
- 调用父类构造器。这个步骤会反复递归进去,直到最祖先的类,依次向下调用构造器。
- 按声明顺序调用成员的初始化构造器方法。
- 调用子类构造器的主体。
可能我说了这个顺序,大家马上就会想到super。是的没错,super()确实可以显示的调用父类中自己想要调用的构造方法,但是super()必须放在构造器的第一行,这个是规定。我们的顺序是没有任何问题的,或者说其实在F的构造器中第一句是super()。只不过我们默认省略了。
(三)协变返回类型特性
java在se5中添加了协变返回类型,它表示在子类中的被覆盖方法可以返回父类这个方法的返回类型的某种子类。
package Polymorphic;
/**
*
* @author QuinnNorris
* 协变返回类型
*/
public class covariant {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
A b = new B();
b.getC().print();
A a = new A();
a.getC().print();
}
}
class A{
public C getC() {
return new C();
}
}
class B extends A{
public D getC(){
return new D();
}
}
class C{
public void print(){
System.out.println("C");
}
}
class D extends C{
public void print(){
System.out.println("D");
}
}
输出结果:
D
C
在上面的例子中,D类是继承于C类的,B类是继承于A类的,所以在B类覆盖的getC方法中,可以将返回类型协变成,C类的某个子类(D类)的类型。
(四)继承设计
通常,继承并不是我们的首选,能用组合的方法尽量用组合,这种手段更灵活,如果你的代码中is-a和is-like-a过多,你就应该考虑考虑是不是该换成has-a一些了。一条通用的准则是:用继承表达行为间的差异,并用字段表达状态上的变化。
而且在用继承的时候,我们会经常涉及到向上转型和向下转型。在java中,所有的转型都会得到检查。即使我们只是进行一次普通的加括号的类型转换,在进入运行期时仍然会对其进行检查,以便保证它的确是我们希望的那种类型。如果不是,就返回一个ClassCastException(类转型异常)。这种在运行期间对类型进行检查的行为称作”运行时类型识别(RTTI)“。
文章转自:http://blog.csdn.net/QuinnNorris/article/details/57413811
Java动态绑定与多态的更多相关文章
- Java 动态绑定和多态
动态绑定和多态 动态绑定是指:"在执行程序期间(而非编译期间),判断引用所指对象的实际类型,调用其相应的方法." 动态绑定(多态)存在的条件 要有继承. 要有重写. 父类引用指向子 ...
- Java——动态绑定和多态
动态绑定是在执行期间(而不是编译期间)判断所用对象的实际类型,根据其实际的类型调用其相应的方法(确定选择哪个多态形式被调用). 首先是方法的参数是父类对象,传入子类对象是否可行 然后引出Parent ...
- java面向对象之 多态 Polymorphism
多态(Polymorphism):用我们通俗易懂的话来说就是子类就是父类(猫是动物,学生也是人),因此多态的意思就是:父类型的引用可以指向子类的对象. 1.多态的含义:一种类型,呈现出多种状态 主要讨 ...
- Java 继承、多态与类的复用
摘要: 本文结合Java的类的复用对面向对象两大特征继承和多态进行了全面的介绍. 首先,我们介绍了继承的实质和意义,并探讨了继承,组合和代理在类的复用方面的异同.紧接着,我们依据继承引入了多态.介绍了 ...
- Java类的多态机制
Java中将一个方法调用同一个方法主体关联起来被称作绑定. 绑定分为前期绑定和后期绑定.前期绑定是在编译器决定的,而后期绑定是在程序运行时决定的.Java中除了static方法和final方法(pri ...
- Java基础之多态和泛型浅析
Java基础之多态和泛型浅析 一.前言: 楼主看了许多资料后,算是对多态和泛型有了一些浅显的理解,这里做一简单总结 二.什么是多态? 多态(Polymorphism)按字面的意思就是“多种状态”.在面 ...
- 通过实例聊聊Java中的多态
Java中的多态允许父类指针指向子类实例.如:Father obj=new Child(); 那么不禁要发问?? 使用这个父类型的指针访问类的属性或方法时,如果父类和子类都有这个名称的属性或方法,哪 ...
- 深度分析:理解Java中的多态机制,一篇直接帮你掌握!
Java中的多态 1 多态是什么 多态(Polymorphism)按字面的意思就是"多种状态".在面向对象语言中,接口的多种不同的实现方式即为多态.用白话来说,就是多个对象调用同一 ...
- 13、java中的多态
1,多态的体现 父类的引用指向了自己的子类对象. 父类的引用也可以接收自己的子类对象.2,多态的前提 必须是类与类之间有关系.要么继承,要么实现. 通常还有一个前提:存在覆盖. 3,多态的好处 多态的 ...
随机推荐
- 支付宝网站即时支付开发,MD5加签名规则处理代码展示
一.如果传入进来的Object对象,最后生成制定格式的字符换 text: list拼接成字符串,map中的所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串,list的元素之间用“| ...
- 【转帖】Office的光荣历史(2)
Office的光荣历史(2) https://www.sohu.com/a/201411215_657550 2017-10-31 10:57 7.MS Office 2000 (Office 9.0 ...
- FTP文件上传下载
使用Apache Commons Net来实现FTP服务器文件的上传 与 下载 maven配置Jar <!-- https://mvnrepository.com/artifact/common ...
- # MATLAB数据处理
目录 MATLAB数据处理 数据归一化处理 冒号的作用(获取指定行列的数据) MATLAB数据处理 mean(A,(b)) %均值函数,b为设置对哪一维上的数据进行处理,默认为第一维(列),行为第二维 ...
- jmeter 工具学习 未完待续
about Apache JMeter是Apache组织的开源项目,是 一个纯Java桌面应用,用于压力测试和性能测试,它最初被设计用于 web应用测试,后来逐渐的扩展到其他领域 jmeter可以用于 ...
- 【hash表】图书管理
[哈希和哈希表]图书管理 题目描述 图书管理是一件十分繁杂的工作,在一个图书馆中每天都会有许多新书加入.为了更方便的管理图书(以便于帮助想要借书的客人快速查找他们是否有他们所需要的书),我们需要设计一 ...
- 2019牛客暑期多校训练营(第二场) - B - Eddy Walker 2 - BM算法
参考于: https://www.luogu.org/problemnew/solution/P4723 shadowice1984 (太难) https://www.cnblogs.com/zhgy ...
- 并不对劲的复健训练-bzoj5250:loj2473:p4365:[九省联考2018]秘密袭击
题目大意 有一棵\(n\)(\(n\leq 1666\))个点的树,有点权\(d_i\),点权最大值为\(w\)(\(w\leq 1666\)).给出\(k\)(\(k\leq n\)),定义一个选择 ...
- Centos7.3安装Oracle11.2.0.3
1.创建用户用户组 [root@smallcloud ~]# groupadd oinstall [root@smallcloud ~]# groupadd dba [root@smallcloud ...
- 史上最全的MySQL高性能优化实战总结!
1.1 前言 MySQL对于很多Linux从业者而言,是一个非常棘手的问题,多数情况都是因为对数据库出现问题的情况和处理思路不清晰.在进行MySQL的优化之前必须要了解的就是MySQL的查询过程,很多 ...