方法的动态分派,不是编译器确定的是在程序运行期确定的。
方法的动态分派涉及到一个重要概念:方法接收者也就是方法具体是由哪个对象调用,多态是一种运行期的行为,这是由于invokevitural 指令的多态查找流程所决定的。
- 首先到操作数寻找栈顶寻找元素所指向对象的实际类型而不是静态类型
- 在实际类型上寻找到符合条件(访问权限、名称和)的方法,找到返回这个方法直接引用
- 如果没有找到就从子类向上依次寻找符合条件方法。
判断动态还是静态调用最好方法就是看方法接收者是否相同。
针对方法调用动态分派:在类的方法区会建议一个虚拟方法表的数据结构(virtual method table, vtable)
针对 invokeinterface 指令,JVM 会建立一个叫做接口方法表的数据结构(interface method table itable)
class Base {
public void onStart(){
System.out.println("onStart of Base");
}
}
class Activity extends Base{
@Override
public void onStart() {
System.out.println("onStart of Activity");
}
}
class Fragment extends Base{
@Override
public void onStart() {
System.out.println("onStart of Fragment");
}
}
public class ClientC {
public static void main(String[] args) {
Base activity = new Activity();
Base fragment = new Fragment();
activity.onStart();
fragment.onStart();
activity = new Fragment();
activity.onStart();
}
}
输出
onStart of Activity
onStart of Fragment
onStart of Fragment
字节码
- astore_2 将引用存入到局部变量
- new 关键字在堆内存开辟内存空间,然后执行其构造方法,然后将构造方法返回对对象引用值返回。
- aload_1 从局部变量加载引用,加载索引为 1 应用就是 activity
- invokevirtual : 这是 acitivy 参数是 Base.onStart 但是实际还是调用 Activity 的 onStart。
- aload_2 : 加载 fragment 的引用
- invokevirtual : 调用还是 Base.onStart 的方法。