绑定: 把一个方法与其所在的类/对象 关联起来叫做方法的绑定。分为静态绑定(前期绑定)和动态绑定(后期绑定)
静态绑定: 在程序运行前就已经知道方法属于哪个类,在编译时就可以连接到类中,定位到这个方法 在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对象压入操作数栈,用它来进行调用。而用实例对象进行方法调用的过程就是动态绑定:根据实例对象所属的类型去查找它的方法表,找到匹配的方法进行调用。我们知道,子类中如果重写了父类的方法,则方法表中同名表项会指向子类的方法代码;若无重写,则按照父类中的方法表顺序保存在子类方法表中。故此:动态绑定根据对象的类型的方法表查找方法是一定会匹配(因为编译时在父类方法表中以及查找并匹配成功了,说明方法是存在的。这也解释了为何向上转型时父类引用不能调用子类新增的方法:在父类方法表中必须先对这个方法的存在性进行检验,如果在运行时才检验就容易出危险——可能子类中也没有这个方法)。