静态绑定和动态绑定

多态机制

  多态:在面向对象编程中,某个接口的不同实现,或者说同一个接口使用不同的实例进行不同的操作,下面举一个例子。
  一个父类SuperClass 有不同子类SubClass1,SubClass2,当子类都重写了父类的某个方法。当父类引用指向了不同的子类对象,通过父类引用调用该方法则对应了不同子类对于该方法的实现
父类SuperClass如下所示:

public class SuperClass {
    public void say(){
        System.out.println("This is SuperClass");
    }
}

两个子类如下,都重写了父类的say()

public class SubClass1 extends SuperClass {
    @Override
    public void say() {
        System.out.println("Hello, this is SubClass1");
    }
}
public class SubClass2 extends SuperClass {
    @Override
    public void say() {
        System.out.println("Hello, this is SubClass2");
    }
}

主函数如下所示:声明了三个父类引用,一个指向父类SuperClass实例,其余两个分别指向子类SubClass1,SubClass2实例,从输出中可以看到,虽然三个都是父类引用,但此时调用的say()方法却对应它们各自的实现。

public static void main(String[] args){
        SuperClass superClass = new SuperClass();
        SuperClass subclass= new SubClass();
        superClass.say();
        subclass.say();
    }


为什么会这样呢,由此可以引出本文的主题——Java的绑定机制


  在Java中,绑定可以分为静态绑定和动态绑定,或者称前期绑定和后期绑定。
  静态(前期)绑定即程序编译阶段已经确定,类的实例变量、static变量、static方法、private方法、final方法均属于静态绑定。
  动态(后期)绑定指绑定被推迟到程序运行时。
简单理解,当通过父类引用(即静态类型为父类)访问时,访问的是父类的变量和方法,通过子类引用(即静态类型为子类)访问时,访问的是子类的变量和方法,这称之为静态绑定。
首先是父类,声明了静态变量staticString,实例变量s,静态方法staticMethod(),实例方法say()。

public class SuperClass {
    public static String staticString = "Super class static string";
    public String s = "Super class string";
    public static void staticMethod(){
        System.out.println("Super class static method");
    }

    public void say(){
        System.out.println("Hello, this is SuperClass");
    }
}

然后是子类,子类重写了父类的say()方法,覆盖了成员变量s(关于父子类中同名的静态变量,静态方法还有私有方法,它们之间没有任何关系,不存在重写)。

public class SubClass extends SuperClass {
    public static String staticString = "Sub Class static String";
    public String s = "SubClass Class String";

    public static void staticMethod(){
        System.out.println("Sub class static method");
    }
}

主函数如下所示:

public static void main(String[] args) {
        SuperClass superClass = new SuperClass();
        SuperClass subClass= new SubClass();

        System.out.println("父类引用指向父类对象");
        System.out.println(superClass.s);
        System.out.println(superClass.staticString);
        superClass.staticMethod();
        superClass.say();

        System.out.println("父类引用指向子类对象(即静态类型为父类)");
        System.out.println(subClass.s);
        System.out.println(subClass.staticString);
        subClass.staticMethod();
        subClass.say();
    }

主函数输出如下所示:

从输出结果可以看到,当通过父类引用访问时(静态类型为父类),不管指向的对象是父类类型还是子类类型,此时均为父类实例变量,static变量,static方法。只有子类重写了父类非private方法会进行动态绑定,此时尽管静态类型为父类,调用的却是子类的方法。

动态绑定原理

  动态绑定的实现机制是根据对象的实际类型查找要执行的方法,子类型中找不到的时候再查找父类。 上面代码中通过父类引用subclass执行say()方法时,首先在子类SubClass中寻找say(),找到了则直接执行,否则在父类中寻找。这样就带来了一个问题,当继承的层次比较深时,则调用方法时每次都要从子类一个个往上找,如果要调用的方法位于比较上层的父类,此时效率是比较低的,大多数系统使用一种称为虚方法表的方法来优化调用的效率。

如上图所示,在类加载的时候为每个类创建一个虚方法表,其中记录的是该类对象所有动态绑定的方法及其入口调用地址,每一个方法只有一个表项,当子类重写了父类方法后,只会保留子类的。对于子类SubClass来说,重写了父类SuperClass的say()方法,其虚方法表中的表项指向它自己的代码实现,因此在通过对象动态绑定方法时,不需要从下往上查找每个父类,只需要查找这个表就可以了。

全部评论

相关推荐

点赞 评论 收藏
转发
点赞 收藏 评论
分享
牛客网
牛客企业服务