很多书都说java最终实例方法(不是私有)使用静态绑定,而其他实例方法(非私有)使用动态绑定。但是在编译时,它们都使用“invokevirtual”JVM指令。当JVM执行“invokevirtual”指令时,是否区分最终方法和非最终方法?我原本认为最终方法使用“invokespecial”作为私有实例方法,因为它们都使用静态绑定。
汇编表格是JLS §13.4.17在第13章“二进制兼容性”中的直接结果:
13.4.17.
final
Methods将声明为
final
的方法更改为不再声明为final
不会破坏与预先存在的二进制文件的兼容性。
这意味着调用者的形式不应该反映目标方法是否在编译时是final
(或者当存在不同的表示时,不允许在运行时产生实际差异)。
为了与其他形式的调用进行比较,不能在不破坏与调用者的兼容性的情况下删除static
修饰符,因此,使用专用的invokestatic
指令对调用形式进行编码是一致的。
对于private
方法的调用,调用者必须在同一个类中,因此,在添加或删除private
修饰符时会重新编译(仅考虑有效的调用者),因此在同一个类中调用invokespecial
方法时使用private
没有问题。从JDK 11开始,private
方法可能会被属于同一嵌套组的其他类中的调用者调用;在这种情况下,其他类中的这些调用者不使用invokespecial
。
所以final
和非final
方法之间的区别确实在运行时发生,当JVM知道实际的目标方法时,如果发生这样的区别。符合标准的JVM必须拒绝尝试覆盖final
方法的类,但它们不需要对调用执行优化。在实践中,今天的JVM能够优化所有尚未被覆盖的方法的调用,无论这些属性是否已由final
修饰符强制执行。唯一的区别是将新类加载到JVM中并覆盖非final
方法可能会导致调用者的去优化。
没有其他字节码指令可以调用由invokevirtual
以外的类声明的实例方法。它的名字令人困惑。它也可以命名为invokeClassMethod
或invokeclass
。实际上编译器不会特别处理最终方法。 invokespecial
用于调用构造函数和静态初始化程序。