参考:
多态的底层实现
Java 的方法调用方式
Java 的方法调用有两类,动态方法调用与静态方法调用。静态方法调用是指对于类的静态方法的调用方式,是静态绑定的;而动态方法调用需要有方法调用所作用的对象,是动态绑定的。类调用 (invokestatic) 是在编译时刻就已经确定好具体调用方法的情况,而实例调用 (invokevirtual) 则是在调用的时候才确定具体的调用方法,这就是动态绑定,也是多态要解决的核心问题。
JVM 的方法调用指令有四个,分别是 invokestatic,invokespecial,invokesvirtual 和 invokeinterface。前两个是静态绑定,后两个是动态绑定的。本文也可以说是对于 JVM 后两种调用实现的考察。
实现原理
类调用方法使用的是invokesvirtual 指令。
父类和子类相同的方法的符号引用在各自方法表中偏移量是一样的,比如父类的toString()方法的符号引用在父类的方法表中的偏移量是10,那子类的toString()方法的符号引号在子类方法表中的偏移量也是10,那么调用方法时,JVM根据方法签名在字符串常量池中确定符号引用,然后先找对象的符号引用类型(也就是父类类型)方法表中该符号引用的偏移量,然后执行对象的实际类型的(也就是子类类型)方法表中相同偏移量的方法。
类调用方法使用的是invokeinterface指令。
但是接口和类的是实现有点不一样,因为类是单继承,所以可以让子类和父类保持相同的方法的符号引用在各自方法表中的偏移量一样,但是因为接口是多实现,可以同时实现多个接口,所以不能保证相同的方法的符号引用在各自方法表中的偏移量一样,所以在查找到接口的符号引用的偏移量之后,需要根据根据得到的符号引用去子类的方法表中查询,找到等价的符号引用,然后执行子类的方法。因为多一个查找的步骤,所以接口的多态调用一般比类的多态调用慢一些。