封装,继承,多态 是Java 的三大特性,其中最复杂的则是多态,而在学习多态时,我们不可避免地会遇到一个概念—— Java的动态绑定机制。
本文,我们会使用一个例子,简单的为大家分析一下动态绑定机制。
动态绑定的引出
要学多态,就肯定要学继承,这里相信大家已经学过了继承
下面请看例子:
这里有一个父类A
这里有一个子类B
然后在测试类里调用
请大家思考输出什么?
这里肯定难不倒大家,我们给A类创建了对象a,并调用getI()方法,查看上面的代码,很显然,输出的肯定是A类中属性i的值 10.
同理,下面的输出的是A类中属性i的值 20.
接下来我们加大难度
我们再在测试类中创建一个对象a1,这回输出什么?????????????????????
我们仔细观察,这个对象的定义跟正常的好像不一样~
我们平时定义对象都是 A 某某 = new A(); 或者 B 某某 = new B();
也就是前面和后面写的都是同一个类,表示某某 是类A或者B 的对象。
而这个对象的创建,前面是A,后面是B,那到底这个对象是属于A还是B??
对象 a1 又调用了方法sum 和 sum1,而类A,B里都有这两个方法,那么到底调用的是哪个??
没关系,实践是检验真理的唯一标准,我们运行一下便知!
再回头看一下代码,如果是A类的方法,那么结果就应该是20,30
如果是B类的方法,那结果就应该是40.30
我们运行一下
结果出来了:40,30
那真相大白!对象a1 在执行时调用了类B的方法!
这里就涉及到了动态绑定的知识
要说动态绑定,我们必须先介绍一下 编译类型和运行类型
编译类型和运行类型
这两就是编译类型相同的
而我们刚才重点说的则是编译类型与运行类型不同的例子
这里对象a1的编译类型为A类,而运行类型则为B类
(1)一个对象的编译类型和运行类型可以不一致
(2)编译类型在定义对象时,就确定了,不能改变
(3)运行类型是可以变化的.
(4)编译类型看定义时=号 的左边, 运行类型看 =号的 右边
而从上面的例子我们就可以看出,在调用对象A1的方法时,实际上调用的是它的运行类型(B类)对应的方法
动态绑定
动态绑定的核心原理
-
对象类型决定方法调用
当调用一个对象的实例方法时,JVM 会根据对象的实际类型(运行时类型)决定调用哪个类的方法,而不是根据引用变量的声明类型。 -
方法表(vtable)机制
Java 通过方法表(每个类维护一个方法表)实现动态绑定。每个类的方法表中存储了该类和其父类的所有可访问方法的实际入口地址。调用方法时,JVM 根据对象的实际类型查找方法表,找到对应的方法。
接下来,我们接着上面的例子,再加大难度~!
上面我们已经知道。这几个语句调用的是B类的方法。那我们把B类里相应的方法给注释掉,再想想会输出什么
我们一个一个分析,首先我们要输出a1.sum()。
我们已经知道。因为a1的运行类型为B类,所以这里它会到B类中去找sum方法。但是sum方法已经被我们注销掉了。所以根据Java的继承机制。它就会到B类的父类,也就是A类中再去找名为sum的方法.
于是它就要运行A类中的sum方法。而这里的sum方法的返回值是 getI方法的返回值再加10。于是他又要去找get I方法.所以这个问题又转变成了跟上面一样的问题。A类和B类。中的get I方法,它到底运行哪一个?
所以根据上面的原则。他又去a1对象的运行类型B类中去运行getI方法了
所以结果也就是 10
我们再分析下一个
同理。对象a1又去B类中找sum1()方法,但B类中没有。于是他又去A类中找。
在A类中找到sum1()方法,它开始运行。方法的返回值为i+10。那么,问题又来了。这个i是A类中的i还是B类中的i ?
我们就需要在回顾一下机制
java的动态绑定机制
(1)当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
(2)当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
i 属于对象的属性(成员变量),没有动态绑定机制~!
或者说属于静态绑定,看的是声明类型,这里 的 i 在类A 中声明,所以值就为10
所以结果为 i+10= 20
我们运行一下看看有没有错
没问题~!
最后我们浅浅比较一下动态绑定和静态绑定
动态绑定和静态绑定的区别
静态方法(static
)、私有方法(private
)、构造方法和 final
方法不参与动态绑定(属于静态绑定)。
下面是本文所用的所有代码(后面的例子要实现需要注释掉B类的 sum()和 sum1()方法)
public class poly04 {
public static void main(String[] args) {
A a = new A();
System.out.println(a.getI());
B b = new B();
System.out.println(b.getI());
A a1 = new B();//向上转型
System.out.println(a1.sum());
System.out.println(a1.sum1());
}
}
//父类
class A {
public int i = 10;
public int getI(){
return i;
}
public int sum(){
return getI()+10;
}
public int sum1(){
return i + 10;
}
}
//子类
class B extends A{
public int i = 20;
public int sum(){
return i + 20;
}
public int sum1(){
return i + 10;
}
public int getI(){
return i;
}
}
=====================结束========================
文章编写不易,求点赞~!
有疑问可以评论~!
点赞~~~~~~~~~~~~~~~~~~~~~~~~~~~~!