【面向面试学习】怎样理解Java的方法分派?

313 阅读2分钟

精简版回答

方法分派就是java程序运行时,jvm是如何确定该调用哪个方法

普通版回答

  • 遇到静态方法编译时确定调用,完成方法分派
  • private方法编译时确定调用,完成分派
  • 构造方法编译时确定调用,完成分派
  • Interface的方法,运行时动态分派
  • Override的方法,运行时动态分派
  • 反射调用方法,运行时动态分派
  • Java8新增lambda方法,运行时动态分派

豪华版回答

Java类加载到卸载流程

  1. 加载
  2. 验证
  3. 准备
  4. 解析
  5. 初始化
  6. 使用
  7. 卸载

方法表(Method Table)

  • 父类的方法比子类的方法先得到解析,即父类的方法相对于子类的方法位于表的前列。
  • 表中每项对应于一个方法,索引到实际方法的实际代码上。
  • 如果子类重写了父类中某个方法的代码,则该方法第一次出现的位置的索引更换到子类的实现代码上,而不会在方法表中出现新的项。
  • JVM运行时,当代码索引到一个方法时,是根据它在方法表中的偏移量来实现访问。(第一次执行到调用指令时,会执行解释,将符号索引替换为对应的直接索引)。

在不考虑反射的情况下,class文件中由以下五个指令负责调用类的方法:

  • invokestatic 用于调用静态方法的指令,在解析阶段就能确定调用哪个方法
  • invokerspecial 用于调用私有或构造方法的指令,在解析阶段就能确定调用哪个方法
  • invokeinterface 跟invokevirtual类似;
  • invokevirtual 用于调用多态方法,运行时才能确定调用哪个
  • invokedynamic JDK7引入,JDK8正式应用,支持lambda,可以在运行时再决定由哪个类来接收被调用的方法

invokevirtual工作方式

  • 被调用方法的对象的类的实现中查找对应的方法,如果没找到,则去父类查找,找到函数并实现调用,而不是依赖于引用的类型,例如Class A=new Class B(),A.fun(),会从B及其父类里找找个方法,去匹配调用
  • 配合方法表工作

invokeinterface与invokevirtual的比较

当使用invokeinterface来调用方法时,由于不同的类可以实现同一interface,我们无法确定在某个类中的interface中的方法处在哪个位置。于是,也就无法解释CONSTANT_interfaceMethodref-info为直接索引,而必须每次都执行一次在methodtable中的搜索了。所以,在这种实现中,通过invokeinterface访问方法比通过invokevirtual访问明显慢很多。

参考文献