Java -- 静态绑定和动态绑定

    科技2022-07-11  102

    Java – 静态绑定和动态绑定

    Java – 静态绑定和动态绑定 - 1. 概念 - 2. 静态绑定 - 3. 动态绑定 - 4. 小结

    1. 概念

    绑定: 把一个方法与其所在的类/对象 关联起来叫做方法的绑定。分为静态绑定(前期绑定)和动态绑定(后期绑定)

    静态绑定: 在程序运行前就已经知道方法属于哪个类,在编译时就可以连接到类中,定位到这个方法   在Java中,final,private,static修饰的方法以及构造函数都是静态绑定,不需要程序运行,不需具体的实例对象就可以知道这个方法的具体内容 private: 私有,被private修饰的方法是无法由本类之外的其他类所调用的,也就是本类特有,所以就由编译器识别方法属于哪个类

    public class Person { private String talk; private String canTalk(){ return talk; } } class Animal{ public static void main(String[] args) { Person p = new Person(); // private 修饰的方法是Person类独有的,所以Animal类无法访问(动物本来就不能说话) // p.canTalk(); } }

    final: final修饰的方法不能重写,但可以由子类调用,生命为final可以关闭动态绑定

    public class Fruit{ private String fruitName; final String eatingFruit(String name){ System.out.println("eating" + name); return fruitName; } } class Apple extends Fruit{ // 不能重写final方法,eatingFruit只属于Fruit类,Apple类无法调用 // final String eatingFruit(String name){ // super.eatingFruit(name); // } String eatingApple(String name){ return super.eatingFruit(name); } }

    static: static修饰的方法只能通过类名.变量名直接调用,这样的话类名就是确定的,并不会产生多态

    public class SuperClass{ public static void sayHello(){ System.out.println("由 superclass 说你好"); } } public class SubClass extends SuperClass{ public static void sayHello(){ System.out.println("由 SubClass 说你好"); } public static void main(String[] args){ SuperClass.sayHello(); SubClasss.sayHello(); } }

     

    动态绑定: 在程序运行过程中,根据具体的实例对象才能具体确定哪个方法 动态绑定是多态性的关键,它通过方法表实现: 每个类被加载到虚拟机的时候,在方法区保存元数据,包括一个叫做 方法表(method table) 的东西,表中记录了这个类定义的方法的指针,每个表项指向一个具体的方法。

    绑定过程: 1.虚拟机提取对象的实际类型的方法表 2.虚拟机搜索方法签名 3.调用方法

    动态绑定发生在父子类的转换声明之下:

    Parent p = new Children(); p.say(); Children 继承自Parent,重写了say; 过程:

    编译:向上转型时,用父类引用执行子类对象,并可以用父类引用调用子类中重写了的同名方法,但是不能调用子类中新增的方法; 因为在代码的编译阶段,编译器通过 声明对象的类型(即引用本身的类型) 在方法区中该类型的方法表中查找匹配的方法(最佳匹配法:参数类型最接近的被调用),如果有则编译通过。编译阶段是确保方法的正确性,保证程序能顺利安全的运行运行:我们又知道,ft.say()调用的是Son中的say(),这不就与上面说的,查找Father类的方法表的匹配方法矛盾了吗?不,这里就是动态绑定机制的真正体现、上面编译阶段在 声明对象类型 的方法表中查找方法,只是为了安全地通过编译(也为了检验方法是否是存在的)。而在实际运行这条语句时,在执行 Parent p = new Children(); 这一句时创建了一个Children实例对象,然后在 p.say() 调用方法时,JVM会把刚才的son对象压入操作数栈,用它来进行调用。而用实例对象进行方法调用的过程就是动态绑定:根据实例对象所属的类型去查找它的方法表,找到匹配的方法进行调用。我们知道,子类中如果重写了父类的方法,则方法表中同名表项会指向子类的方法代码;若无重写,则按照父类中的方法表顺序保存在子类方法表中。故此:动态绑定根据对象的类型的方法表查找方法是一定会匹配(因为编译时在父类方法表中以及查找并匹配成功了,说明方法是存在的。这也解释了为何向上转型时父类引用不能调用子类新增的方法:在父类方法表中必须先对这个方法的存在性进行检验,如果在运行时才检验就容易出危险——可能子类中也没有这个方法)。

    小结

    程序在JVM运行过程中,会把类的类型信息,static属性和方法,final常量等元数据加载到方法区,这些在类被加载时就已经知道,不需要对象的创建就能访问就是静态绑定;需要等数据创建出来,使用时根据堆中的实例对象的类型才进行取用的就是动态绑定的内容
    Processed: 0.013, SQL: 8