1、重载(静态绑定)
类名、方法名以及方法描述符(method descriptor)形成重载。
方法描述符:参数类型,返回类型(JVM独有)。
2、重写(动态绑定)
当子类和父类中方法名和方法描述符都一致形成重写。
重写在jvm识别方法时无法直接识别,要根据调用者的类型。
3、寻找方法
public static void main(String[] args) {
try {
C c = () -> true; A a = new B();
double price = a.discountedPrice(5.0, c);
System.out.println("price:" + price);
}catch (Exception e){
}
}
public class Test {
public Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: invokedynamic #2, 0 // InvokeDynamic #0:isVIP:()LC;
5: astore_1
6: new #3 // class B
9: dup
10: invokespecial #4 // Method B."<init>":()V
13: astore_2
14: aload_2
15: ldc2_w #5 // double 5.0d
18: aload_1
19: invokevirtual #7 // Method A.discountedPrice:(DLC;)D
22: dstore_3
23: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
26: new #9 // class java/lang/StringBuilder
29: dup
30: invokespecial #10 // Method java/lang/StringBuilder."<init>":()V
33: ldc #11 // String price:
35: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
38: dload_3
39: invokevirtual #13 // Method java/lang/StringBuilder.append:(D)Ljava/lang/StringBuilder;
42: invokevirtual #14 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
45: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
48: return
}
执行 a.discountedPrice(5.0, c),寻址步骤,如图:
方法表
类加载的准备阶段构造与该类相关联的方法表。分为两种虚方法表(对应invokevirtual指令),接口方法表(对应invokeinterface指令)
方法表本质上是一个数组,每个数组元素指向一个当前类及其祖先类中非私有的实例方法。
这些方法可能是具体的、可执行的方法,也可能是没有相应字节码的抽象方法。
方法表满足两个特质:其一,子类方法表中包含父类方法表中的所有方法;其二,子类方法在方法表中的索引值,与它所重写的父类方法的索引值相同
内联缓存
内联缓存是一种加快动态绑定的优化技术。
它能够缓存虚方法调用中调用者的动态类型,以及该类型所对应的目标方法。
在之后的执行过程中,如果碰到已缓存的类型,内联缓存便会直接调用该类型所对应的目标方法。
如果没有碰到已缓存的类型,内联缓存则会退化至使用基于方法表的动态绑定。