方法调用的过程
我们先来看c.action();,这句代码的执行过程:
1)查看c的对象类型,找到Child类型,在Child类型中找action方法,发现没有,到父类中寻找;
2)在父类Base中找到了方法action,开始执行action方法;
3)action先输出了start,然后发现需要调用step()方法,就从Child类型开始寻找step()方法;
4)在Child类型中找到了step()方法,执行Child中的step()方法,执行完后返回action方法;
5)继续执行action方法,输出end。
寻找要执行的实例方法的时候,是从对象的实际类型信息开始查找的,找不到的时候,再查找父类类型信息。
我们来看b.action(),这句代码的输出和c.action()是一样的,这称为动态绑定,而动态绑定实现的机制就是根据对象的实际类型查找要执行的方法,子类型中找不到的时候再查找父类。
这里,因为b和c指向相同的对象,所以执行结果是一样的。
如果继承的层次比较深,要调用的方法位于比较上层的父类,则调用的效率是比较低的,因为每次调用都要进行很多次查找。大多数系统使用一种称为虚方法表
的方法来优化调用的效率。
所谓虚方法表,就是在类加载的时候为每个类创建一个表,记录该类的对象所有动态绑定的方法(包括父类的方法)及其地址,但一个方法只有一条记录,子类重写了父类方法后只会保留子类的。对于本例来说,Child和Base的虚方法表如图4-5所示。
图4-5 继承原理:虚方法表
对Child类型来说,action方法指向Base中的代码,toString方法指向Object中的代码,而step()指向本类中的代码。当通过对象动态绑定方法的时候,只需要查找这个表就可以了,而不需要挨个查找每个父类。接下来,我们介绍变量访问的过程。