概述
方法查找
对于非接口符号引用,假定该符号引用所指向的类为 C,则 Java 虚拟机会按照如下步骤进行查找。
在 C 中查找符合名字及描述符的方法。
如果没有找到,在 C 的父类中继续搜索,直至 Object 类。
如果没有找到,在 C 所直接实现或间接实现的接口中搜索,这一步搜索得到的目标方法必须是非私有、非静态的。并且,如果目标方法在间接实现的接口中,则需满足 C 与该接口之间没有其他符合条件的目标方法。如果有多个符合条件的目标方法,则任意返回其中一个。
从这个解析算法可以看出,静态方法也可以通过子类来调用。此外,子类的静态方法会隐藏(注意与重写区分)父类中的同名、同描述符的静态方法。
对于接口符号引用,假定该符号引用所指向的接口为 I,则 Java 虚拟机会按照如下步骤进行查找。在 I 中查找符合名字及描述符的方法。如果没有找到,在 Object 类中的公有实例方法中搜索。如果没有找到,则在 I 的超接口中搜索。这一步的搜索结果的要求与非接口符号引用步骤 3 的要求一致。经过上述的解析步骤之后,符号引用会被解析成实际引用。对于可以静态绑定的方法调用而言,实际引用是一个指向方法的指针。对于需要动态绑定的方法调用而言,实际引用则是一个方法表的索引.
动态方法表
方法表本质上是一个数组,每个数组元素指向一个当前类及其祖先类中非私有的实例方法。
这些方法可能是具体的、可执行的方法,也可能是没有相应字节码的抽象方法。方法表满足两个特质:其一,子类方法表中包含父类方法表中的所有方法;其二,子类方法在方法表中的索引值,与它所重写的父类方法的索引值相同。
方法调用指令中的符号引用会在执行之前解析成实际引用。对于静态绑定的方法调用而言,实际引用将指向具体的目标方法。对于动态绑定的方法调用而言,实际引用则是方法表的索引值(实际上并不仅是索引值)
在执行过程中,Java 虚拟机将获取调用者的实际类型,并在该实际类型的虚方法表中,根据索引值获得目标方法。这个过程便是动态绑定。
代码示例:
abstract class Passenger {abstract void passThroughImmigration();@Overridepublic String toString() { ... }}class ForeignerPassenger extends Passenger {@Overridevoid passThroughImmigration() { /* 进外国人通道 */ }}class ChinesePassenger extends Passenger {@Overridevoid passThroughImmigration() { /* 进中国人通道 */ }void visitDutyFreeShops() { /* 逛免税店 */ }}Passenger passenger = ...passenger.passThroughImmigration();

桥接方法
示例
Java编译器自动生成的方法,举一个方法重写的场景。
interface Customer {boolean isVIP();}class VIP implements Customer {@Overridepublic boolean isVIP() {return true;}}class Merchant<T extends Customer> {public double actionPrice(double price, T customer) {return 0;}}class VIPOnlyMerchant extends Merchant<VIP> {@Overridepublic double actionPrice(double price, VIP customer) {return 0;}}class Main {public static void main(String[] args) {Merchant m = new VIPOnlyMerchant();m.actionPrice(1.0, new VIP());m.actionPrice(1.0, new Customer() {@Overridepublic boolean isVIP() {return false;}});}}
javap -v VIPOnlyMerchant.class
编译后的VIPOnlyMerchant,生成了一个桥接方法,方法签名为:
ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
{jvm.VIPOnlyMerchant();descriptor: ()Vflags:Code:stack=1, locals=1, args_size=10: aload_01: invokespecial #1 // Method jvm/Merchant."<init>":()V4: returnLineNumberTable:line 40: 0LocalVariableTable:Start Length Slot Name Signature0 5 0 this Ljvm/VIPOnlyMerchant;public double actionPrice(double, jvm.VIP);descriptor: (DLjvm/VIP;)Dflags: ACC_PUBLICCode:stack=2, locals=4, args_size=30: dconst_01: dreturnLineNumberTable:line 43: 0LocalVariableTable:Start Length Slot Name Signature0 2 0 this Ljvm/VIPOnlyMerchant;0 2 1 price D0 2 3 customer Ljvm/VIP;MethodParameters:Name Flagspricecustomer---- 生成的桥接方法public double actionPrice(double, jvm.Customer);descriptor: (DLjvm/Customer;)Dflags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETICCode:stack=4, locals=4, args_size=30: aload_01: dload_12: aload_33: checkcast #2 // class jvm/VIP6: invokevirtual #3 // Method actionPrice:(DLjvm/VIP;)D9: dreturnLineNumberTable:line 40: 0LocalVariableTable:Start Length Slot Name Signature0 10 0 this Ljvm/VIPOnlyMerchant;MethodParameters:Name Flagsprice syntheticcustomer synthetic}
所以 编译后的VIPOnlyMerchant类有如下两个方法:
public double actionPrice(double price, VIP customer)public double actionPrice(double price, Customer customer)
所以什么是桥接方法
When compiling a class or interface that extends a parameterized class or implements a parameterized interface, the compiler may need to create a synthetic method, called a bridge method, as part of the type erasure process. You normally don’t need to worry about bridge methods, but you might be puzzled if one appears in a stack trace. see: https://docs.oracle.com/javase/tutorial/java/generics/bridgeMethods.html
所以:基本上可以不用关注

