怎么理解java动态绑定_Java学习笔记之深入理解动态绑定和静态绑定

基本概念

动态绑定和静态绑定是Java中两个重要的概念。首先思考这么一个问题,当一个类中存在方法名相同但参数不同(重载)的函数或同一类层次结构下同一名称的方法(重写),程序在执行的时候该如何辨别区分呢?这里就需要用到Java中的动态绑定和静态绑定来解决。那么什么是动态绑定和静态绑定呢?

绑定:指一个方法的调用与方法所在的类关联起来

静态绑定:方法在程序编译期进行绑定

动态绑定:方法在程序运行时根据具体对象的类型进行绑定

静态绑定 VS 动态绑定

静态绑定发生在编译期(Compile time),而动态绑定发生在运行时(Runtime)

private, final and static方法和变量使用静态绑定,而虚函数(virtual methods)则会根据运行时的具体对象进行绑定

静态绑定使用的是类信息,而动态绑定使用的是对象信息

重载方法(overloaded methods)使用的是静态绑定,而重写方法(overridden methods)使用的是动态绑定

虚函数:在Java语言中, 所有的方法默认都是”虚函数”。只有以关键字 final 标记的方法才是非虚函数。虚函数是面向对象编程实现多态的基本手段。

静态绑定示例

例1:

class A {

public void hello(String str) {

System.out.println("a String instance in A");

}

public void hello(Object str) {

System.out.println("a Object instance in A");

}

}

public class BindingDemo {

public static void main(String args[]) {

String str = "";

Object obj = "";

A a = new A();

a.hello(str);

a.hello(obj);

}

}

输出结果:

a String instance in A

a Object instance in A

分析:可以看出,在A中有两个同名但参数不同的方法(重载),在调用方法a.hello()时,程序会自动根据输入的参数类型来选择具体调用哪个方法,其后的原理就是静态绑定,即在编译期根据参数类型进行静态绑定。

动态绑定实例

例2:

class A {

public void hello() {

System.out.println("hello in A");

}

}

class B extends A {

@Override

public void hello() {

System.out.println("hello in B");

}

}

public class StaticBindingDemo {

public static void main(String args[]) {

A a = new B();

a.hello();

}

}

输出结果:

hello in B

分析:B继承于A,并重写了A中的方法hello(),从结果可知,a.hello()调用的不是A中的hello(),而是B中的hello(),这正是因为程序在运行时发生了动态绑定。同时也说明了重写方法使用的是动态绑定。

相关问题探讨

Stack Overflow上探讨了这么一个问题,具体如下:

class A

{

int x = 5;

}

class B extends A

{

int x = 6;

}

class SubCovariantTest extends CovariantTest

{

public B getObject()

{

System.out.println("sub getobj");

return new B();

}

}

public class CovariantTest {

public A getObject()

{

System.out.println("ct getobj");

return new A();

}

public static void main(String[]args)

{

CovariantTest c1 = new SubCovariantTest();

System.out.println(c1.getObject().x);

}

}

输出结果:

sub getobj

5

看到这样的输出结果,很多人可能会很纳闷,从第一个输出结果sub getobj可以看出调用的是B对象,但从x输出值上看,却是A对象,这是怎么一回事?!先不急着解决这个问题,我们先来看下下面这段程序:

class A

{

int x = 5;

public void doSomething() {

System.out.println("A.doSomething()");

}

}

class B extends A

{

int x = 6;

public void doSomething() {

System.out.println("B.doSomething()");

}

}

public class Main {

public static void main(String args[]) {

A a=new B();

System.out.println(a.x);

a.doSomething();

}

}

输出结果:

5

B.doSomething()

分析:根据前面讲解的静态绑定和动态绑定知识,我们可以清楚的知道,在调用方法a.doSomething()时发生的是动态绑定,故输出结果是B.doSomething(),但当调用a.x时,又发生了些什么呢?! 这里,我们需要了解一个概念:在Java中,变量(fields)没有多态这个说法,只有方法(methods)有。即变量不存在动态绑定,它是在编译期进行绑定。 在编译期,变量x和方法doSomething()都是和A类进行绑定的,而在程序运行时,doSomething()方法会根据运行的对象类型进行动态绑定,即绑定B类,从而执行的是B类中的doSomething()方法。而x变量不进行动态绑定,所以执行的还是A类中的x变量。 再回到最开始的那个问题:在编译期,c1绑定的是类CovariantTest,则c1.getObject()返回的是类A,故c1.getObject().x指的是A.x;在运行时,c1.getObject()则会根据运行的对象进行动态绑定,即绑定类SubCovariantTest,而c1.getObject().x不会在进行动态绑定,所以x的值还是类A中的x的值。

有人可能对变量x到底是属于哪个类还是很疑惑,那么我们再通过一个例子来进行说明:虽然类A和类B都有一个变量x,但是它们并不是同一个x,既然这样,我们就可以用不同的名称表示这两个变量,将类B中的变量x改为y,具体如下

class A {

int x = 5;

}

class B extends A {

int y = 6;

}

class SubCovariantTest extends CovariantTest {

public B getObject() {

System.out.println("sub getobj");

return new B();

}

}

public class CovariantTest {

public A getObject() {

System.out.println("ct getobj");

return new A();

}

public static void main(String[] args) throws java.lang.Exception {

CovariantTest c1 = new SubCovariantTest();

System.out.println(c1.getObject().y);

}

}

在编译期,如果c1.getObject()返回的是类B,那么上面程序完全没问题,但实际上,会出现以下编译错误。说明,在编译期,c1.getObject()返回的是类A,c1.getObject().x指的是A.x,而且在运行期间不会发生改变。

0818b9ca8b590ca3270a3433284dd417.png

上面的问题解决了,可能有人还会继续问,那么怎样才能达到变量被重写的效果?我们可以这样实现

class A {

int x = 5;

public int getX() {

return x;

}

public void doSomething() {

System.out.println("A.doSomething()");

}

}

class B extends A {

int x = 6;

public int getX() {

return x;

}

public void doSomething() {

System.out.println("B.doSomething()");

}

}

public class Main {

public static void main(String args[]) {

A a = new B();

System.out.println(a.getX());

a.doSomething();

}

}

输出结果:

6

B.doSomething()

显然,正是利用方法的动态绑定达到变量被重写的效果。

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值