精简版回答
方法分派就是java程序运行时,jvm是如何确定该调用哪个方法
普通版回答
- 遇到静态方法编译时确定调用,完成方法分派
- private方法编译时确定调用,完成分派
- 构造方法编译时确定调用,完成分派
- Interface的方法,运行时动态分派
- Override的方法,运行时动态分派
- 反射调用方法,运行时动态分派
- Java8新增lambda方法,运行时动态分派
豪华版回答
Java类加载到卸载流程
- 加载
- 验证
- 准备
- 解析
- 初始化
- 使用
- 卸载
方法表(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访问明显慢很多。