Java学习笔记之深入理解动态绑定和静态绑定

来源:互联网 发布:cs编程里包括什么 编辑:程序博客网 时间:2024/05/31 19:05

基本概念

动态绑定和静态绑定是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 Aa 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 getobj5

看到这样的输出结果,很多人可能会很纳闷,从第一个输出结果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();    }}

输出结果:

5B.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,而且在运行期间不会发生改变。

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

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();    }}

输出结果:

6B.doSomething()

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

参考资料

  1. Java中的静态绑定和动态绑定
  2. Java中的静态绑定和动态绑定
  3. What is Static and Dynamic binding in Java with Example
0 0
原创粉丝点击