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