是否可以通过字节码定义方法绑定类型?

Is it possible to define method binding type by bytecode?

来源如下:

https://www.amazon.com/Java-Complete-Reference-Herbert-Schildt/dp/0071808558

第 8 章:使用带有继承的 final

http://javarevisited.blogspot.com.by/2012/03/what-is-static-and-dynamic-binding-in.html

Static Vs. Dynamic Binding in Java

for private,static,final methods early(static) method binding should be used

。所以我创建了一个小测试

class MethodRefDemo2
{
  public static void main( String args[] )
  {
    BindingTest bindingTest = new BindingTest();
    bindingTest.printEarly();
    bindingTest.printLate();
  }
}

class BindingTest
{
  private String early = "static";
  private String late = "dynamic";

  final String printEarly()
  {
    return early;
  }

  String printLate()
  {
    return late;
  }
}

所以按照我的想法,这两个方法应该有不同的绑定类型。检查字节码:

 public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 8 L0
    NEW spring/BindingTest
    DUP
    INVOKESPECIAL spring/BindingTest.<init> ()V
    ASTORE 1
   L1
    LINENUMBER 9 L1
    ALOAD 1
    INVOKEVIRTUAL spring/BindingTest.printEarly ()Ljava/lang/String;
    POP
   L2
    LINENUMBER 10 L2
    ALOAD 1
    INVOKEVIRTUAL spring/BindingTest.printLate ()Ljava/lang/String;
    POP
   L3
    LINENUMBER 11 L3
    RETURN
   L4
    LOCALVARIABLE args [Ljava/lang/String; L0 L4 0
    LOCALVARIABLE bindingTest Lspring/BindingTest; L1 L4 1
    MAXSTACK = 2
    MAXLOCALS = 2

在这里我看到了两条 INVOKEVIRTUAL 指令。那么有什么办法可以确定class字节码使用了什么样的绑定呢?如果不是,我如何确定绑定类型?

短语“来自 java 规范”具有误导性,因为没有多个规范,所以只有 the Java specification 不包含引用的声明。

术语“早期绑定”和“后期绑定”不足以描述 Java 方法调用的可能性。

  1. 在编译时解析最终目标的唯一方法调用是 private 方法和构造函数调用,并且自 Java 8 以来,static 方法调用在 interface 类型上。

  2. 对于非interface类型的static方法调用和super方法调用,将在编译时设置目标类型,但是JVM 可能会在运行时解析指定类型的超类型中的方法。尽管如此,已解析的目标方法在特定的运行时永远不会改变,并且不需要为每次方法调用都解析,所以一旦方法被解析,它就是某种早期绑定。但是 JVM 通常将解析推迟到调用指令的第一次实际执行。

  3. 对于实例方法的其余调用类型,目标方法是否在编译时声明final无关紧要。这些调用总是以相同的方式编码,如 invokevirtual 指令,这意味着根据接收者对象类型查找实际的目标方法。您可以在编译调用程序后将方法更改为 final 或非 final 而不会破坏兼容性(除非您 重写 一个转向 final 之后)。

    当接收者类型是 interface 时,它永远不会在编译时被认为是 final,但在运行时仍然以 final 方法结束。

    当然,在解析之后,JVM 可能会注意到目标方法已被声明 final 并转向匹配上述第二种“早期绑定”的优化调用,另一方面,大多数JVM 足够聪明,可以对尚未声明 [​​=16=] 但仍未实际覆盖的方法执行相同的操作(这可能适用于所有方法中的大多数)。因此,如果唯一的结果是也应用于其他方法的优化,那么将 final 方法的调用称为“早期绑定”是没有意义的。