Java如何确定多态性中的方法在运行时调用?
虽然多态性的主要原则是从Java如何确定多态性中的方法在运行时调用?,java,jvm,polymorphism,bytecode,Java,Jvm,Polymorphism,Bytecode,虽然多态性的主要原则是从类型方面解耦“what-from-who”,但让我困惑的是,方法调用机制如何在多态性中找到并调用正确的方法体 public class TestRide { public static void ride(Cycle c){ c.ride(); } public static void main(String[] args){ Cycle Cycling = new Cycle(); ride(
类型方面解耦“what-from-who”,但让我困惑的是,方法调用机制如何在多态性中找到并调用正确的方法体
public class TestRide {
public static void ride(Cycle c){
c.ride();
}
public static void main(String[] args){
Cycle Cycling = new Cycle();
ride(Cycling);
Bicycle bi = new Bicycle();
ride(bi);
Tricycle tri = new Tricycle();
ride(tri);
Unicycle uni = new Unicycle();
ride(uni);
}
}
因为在java中,所有的方法绑定都是后期绑定
,除非方法是静态的
,最终的
或私有的
,而后期绑定是由JVM完成的,JVM为每个类预计算方法表
,然后在运行时在正常的方法调用中查找表
但在多态性期间也会发生同样的事情。比如说
假设我有一个带有ride()
方法的泛型类Cycle
class Cycle {
public void ride(){
System.out.println("I'm Riding generic Cycle()");
}
}
我有三个专门的类
BicycleUnicycle
,它们扩展了通用类Cycle,并覆盖了它的ride()
方法
class Bicycle extends Cycle {
public void ride() {
System.out.println("I'm riding Bicycle");
}
}
class Tricycle extends Cycle{
public void ride() {
System.out.println("I'm riding Tricycle ");
}
}
class Unicycle extends Cycle {
public void ride() {
System.out.println("I'm Riding Unicycle ");
}
}
这是测试上述多态性的TestRide
类
public class TestRide {
public static void ride(Cycle c){
c.ride();
}
public static void main(String[] args){
Cycle Cycling = new Cycle();
ride(Cycling);
Bicycle bi = new Bicycle();
ride(bi);
Tricycle tri = new Tricycle();
ride(tri);
Unicycle uni = new Unicycle();
ride(uni);
}
}
输出是
I'm Riding generic Cycle()
I'm riding Bicycle
I'm riding Tricycle
I'm Riding Unicycle
字节码:
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=5, args_size=1
0: new #17 // class com/polymorphism/Cycle
3: dup
4: invokespecial #24 // Method com/polymorphism/Cycle."
<init>":()V
7: astore_1
8: aload_1
9: invokestatic #25 // Method ride:(Lcom/polymorphism/
Cycle;)V
12: new #27 // class com/polymorphism/Bicycle
15: dup
16: invokespecial #29 // Method com/polymorphism/Bicycle
."<init>":()V
19: astore_2
20: aload_2
21: invokestatic #25 // Method ride:(Lcom/polymorphism/
Cycle;)V
24: new #30 // class com/polymorphism/Tricycle
27: dup
28: invokespecial #32 // Method com/polymorphism/Tricycl
e."<init>":()V
31: astore_3
32: aload_3
33: invokestatic #25 // Method ride:(Lcom/polymorphism/
Cycle;)V
36: new #33 // class com/polymorphism/Unicycle
39: dup
40: invokespecial #35 // Method com/polymorphism/Unicycl
e."<init>":()V
43: astore 4
45: aload 4
47: invokestatic #25 // Method ride:(Lcom/polymorphism/
Cycle;)V
50: return
我认为@JBNizet已经在评论中找到了解决方案(我的猜测结果是错误的)。但既然他没有把它作为答案发布,我就这么做:
main
方法不应该显示任何动态行为,因为它总是调用单个方法TestRide.ride(循环c)
。没有其他可能的方法,所以没有什么要弄清楚的
动态方法调用位于该方法的内部
TestRide.ride(循环c)
。现在您已经发布了该代码,实际上我们看到了一个使用invokevirtual
的动态方法分派。所以,毕竟,没有什么意外。动态方法调度就在那里。首先invokedynamic
用于Java8lambda和非Java代码,所以您可以忽略这一点
除此之外,还有四条调用指令(invokespecial
,invokestatic
,invokevirtual
,以及invokeinterface
)。您可以在JVM规范中看到精确的语义,但底线是invokevirtual
和invokeinterface
都是虚拟方法调用,也就是说,在运行时根据目标的conrete类型选择调用的实际方法
代码中唯一的虚拟调用是TestRide.ride。列出的目标是循环。行驶:()V
。但是,由于它是一个虚拟调用,JVM将在运行时检查第一个参数的实际类型,并调用该方法的最派生版本
这类似于C++中的虚拟方法调用,除了JVM和JIT编译的抽象允许潜在的更优化的实现。
还要注意,这不能与方法重载混淆,方法重载是编译时多态性的一种形式。对于重载方法,编译器根据参数的编译时类型选择要调用的方法。可能您的测试用例太简单了。如果Java编译器在编译时知道将调用什么方法,则不需要动态执行任何操作的字节码。
invokedynamic
是在JVM字节码中引入的,用于动态方法查找,而不遵循Java的方法查找。为什么Java编译器会使用它?使用invokevirtual
和invokeinterface
完成虚拟方法调度。请注意,如果Java编译器愿意,它当然可以免费使用invokedynamic
。Java语言规范没有说明Java必须如何编译,它甚至没有说它必须被编译,它也可以被解释。您正在发布main()的字节码,其中有0个多态方法调用:只有构造函数调用和静态方法调用(toride()
)。多态方法调用是在TestRide.ride()
@mastov no中进行的。它对静态方法ride()进行了4次invokestatic调用,以一个循环作为参数。基类没有发现任何东西。invokevirtual有一个Cycle对象,在该对象上调用ride()方法作为堆栈上的第一个参数。它获取该Cycle对象的具体类(例如,获取BiCycle或TriCycle)。然后按照中描述的过程在该具体类中查找ride()方法