将一个方法调用同一方法主体关联进来就叫做绑定。如果在程序执行前进行绑定由编译器和连接程序实现,叫做前期绑定。这个是在面向过程的语言中不需要选择就默认的绑定方式。现在我对面各过程语言已经很模糊了,高中是只知道面向过程依稀记得那时用的好像是什么Fox或Fox什么的。
在运行时根据对象的类型进行绑定,叫做后期绑定也称动态绑定或运行时绑定。如果一种语言想实现后期绑定,,就必须具有某种机制,以便在运行时能判断对象的类型,从面调用恰当的方法。也就是说编译器一直不知道对象的类型,但是方法调用机制能找到正确的方法体,产加以调用。后期绑定机制随语言的不同而有所不同,介是只要想一下就会得知,不管怎样都必须在对象中安置某种“类型信息”。
Java中除了static和final方法(private方法属于final方法)之外,其他所有方法都是后期绑定。所以java中所有方法都是通过动态绑定实现多态的,所以就可以编写只与基类打交道的程序代码了,并且这些代码对所有的导出类都可以正确运行。或者说是发送消息给某个对象,让该对象去断定应该做什么事。
上例子说话:
package com.ebao.java.dynamic.binding;
public class Shape {
public void draw(){}
public void erase(){}
}
public class Circle extends Shape {
public void draw(){
System.out.println("========Circle.draw()==");
}
public void erase(){
System.out.println("========Circle.erase()==");
}
}
public class Square extends Shape {
public void draw(){
System.out.println("========Square.draw()==");
}
public void erase(){
System.out.println("========Square.erase()==");
}
}
public class Trangle extends Shape {
public void draw(){
System.out.println("========Trangle.draw()==");
}
public void erase(){
System.out.println("========Trangle.erase()==");
}
}
import java.util.Random;
public class RandomShapeGenerator {
private Random ran = new Random(47);
public Shape next(){
switch(ran.nextInt(3)){
default:
case 0:return new Circle();
case 1:return new Square();
case 2:return new Trangle();
}
}
}
public class TestShape {
private static RandomShapeGenerator gen = new RandomShapeGenerator();
public static void main(String[] args){
Shape[] s = new Shape[9];
for(int i=0;i<s.length;i++){
s[i] = gen.next();
}
for(Shape shp:s)
shp.draw();
}
}
运行结果:
========Trangle.draw()==
========Trangle.draw()==
========Square.draw()==
========Trangle.draw()==
========Square.draw()==
========Trangle.draw()==
========Square.draw()==
========Trangle.draw()==
========Circle.draw()==
RandomShapeGenerator是一种“工厂”(factory),在每次调用next()方法时,它可以为随机选择的Shape对象产生一引用。请注意向上转型是在return语句里发生的。每个return语句取得一个指向某个Circle、Square、Triangle的引用,并将其以Shape类型从next()方法中发送出去。所以无论什么调用next()方法时,是绝对不可能知道具体类型到底是什么的。因为我们总是只能获得一通用的Shape引用。所以随机选择很好的说明了,在编译时,编译器不需要获得任何特殊信息就能进行正确的调用。对draw()方法的所有调用都是通过动态绑定进行的。
示例二: 创建一个包含两个方法的基类。在第一个方法中可以调用第二个方法。然后产生一个继承自该类的导出类,且覆盖基类中的第二个方法。为该导出类创建一个对象,将其向上转型到基类开并调用第一个方法。
package com.ebao.java.dynamic.binding;
public class Shape1 {
public void draw(){
System.out.println("-------------------Shape1.draw()");
this.erase();
}
public void erase(){
System.out.println("-------------------Shape1.erase()");
}
}
public class TestShape1 extends Shape1 {
public void erase(){
System.out.println("-------------------TestShape1.erase()");
}
public static void main(String[] args){
Shape1 s1 = new TestShape1();
s1.draw();
}
}
运行结果:
-------------------Shape1.draw()
-------------------TestShape1.erase()
由此可看出向上转型后的引用不管调用的方法有没有被导出类覆盖,整个过程中只要涉及到的方法被导出类覆盖了,动态绑定的就是导出类的方法。