是否可以通过字节码定义方法绑定类型?
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 方法调用的可能性。
在编译时解析最终目标的唯一方法调用是 private
方法和构造函数调用,并且自 Java 8 以来,static
方法调用在 interface
类型上。
对于非interface
类型的static
方法调用和super
方法调用,将在编译时设置目标类型,但是JVM 可能会在运行时解析指定类型的超类型中的方法。尽管如此,已解析的目标方法在特定的运行时永远不会改变,并且不需要为每次方法调用都解析,所以一旦方法被解析,它就是某种早期绑定。但是 JVM 通常将解析推迟到调用指令的第一次实际执行。
对于实例方法的其余调用类型,目标方法是否在编译时声明final
无关紧要。这些调用总是以相同的方式编码,如 invokevirtual
指令,这意味着根据接收者对象类型查找实际的目标方法。您可以在编译调用程序后将方法更改为 final
或非 final
而不会破坏兼容性(除非您 重写 一个转向 final
之后)。
当接收者类型是 interface
时,它永远不会在编译时被认为是 final
,但在运行时仍然以 final
方法结束。
当然,在解析之后,JVM 可能会注意到目标方法已被声明 final
并转向匹配上述第二种“早期绑定”的优化调用,另一方面,大多数JVM 足够聪明,可以对尚未声明 [=16=] 但仍未实际覆盖的方法执行相同的操作(这可能适用于所有方法中的大多数)。因此,如果唯一的结果是也应用于其他方法的优化,那么将 final
方法的调用称为“早期绑定”是没有意义的。
来源如下:
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 方法调用的可能性。
在编译时解析最终目标的唯一方法调用是
private
方法和构造函数调用,并且自 Java 8 以来,static
方法调用在interface
类型上。对于非
interface
类型的static
方法调用和super
方法调用,将在编译时设置目标类型,但是JVM 可能会在运行时解析指定类型的超类型中的方法。尽管如此,已解析的目标方法在特定的运行时永远不会改变,并且不需要为每次方法调用都解析,所以一旦方法被解析,它就是某种早期绑定。但是 JVM 通常将解析推迟到调用指令的第一次实际执行。对于实例方法的其余调用类型,目标方法是否在编译时声明
final
无关紧要。这些调用总是以相同的方式编码,如invokevirtual
指令,这意味着根据接收者对象类型查找实际的目标方法。您可以在编译调用程序后将方法更改为final
或非final
而不会破坏兼容性(除非您 重写 一个转向final
之后)。当接收者类型是
interface
时,它永远不会在编译时被认为是final
,但在运行时仍然以final
方法结束。当然,在解析之后,JVM 可能会注意到目标方法已被声明
final
并转向匹配上述第二种“早期绑定”的优化调用,另一方面,大多数JVM 足够聪明,可以对尚未声明 [=16=] 但仍未实际覆盖的方法执行相同的操作(这可能适用于所有方法中的大多数)。因此,如果唯一的结果是也应用于其他方法的优化,那么将final
方法的调用称为“早期绑定”是没有意义的。