【发布时间】:2015-12-17 04:35:41
【问题描述】:
虽然多态性的主要原理是用types 来解耦“what from who”,但让我感到困惑的是,方法调用机制是如何在多态性中找出并调用正确的方法体的。
由于在java中所有方法绑定都是late-binding,除非方法是static、final或private,并且后期绑定由JVM完成,它为每个类预先计算method table然后做一个表在运行时在正常的方法调用中查找。
但是在多态性期间也会发生同样的事情。例如
假设我有一个带有ride() 方法的通用类Cycle
class Cycle {
public void ride(){
System.out.println("I'm Riding generic Cycle()");
}
}
我有三个专业类BicycleTricycle 和Unicycle,它们扩展了通用类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
即使在字节码中,它也像往常一样使用invokestatic 和invokespecial 调用方法,而我认为它会使用invokedynamic 来确定适合对象实际类型的方法版本。但事实并非如此。
那么当我们在ride() 方法中像ride(bi) 那样在TestRide 类中传递一个向上转换的对象时,Java 是如何计算多态性期间的实际方法调用的呢?
编辑:RIDE 方法字节码
public static void ride(com.polymorphism.Cycle);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokevirtual #16 // Method com/polymorphism/Cycle.r
ide:()V
4: return
【问题讨论】:
-
可能你的测试用例太简单了。如果 Java 编译器在编译时就知道会调用什么方法,则不需要动态执行任何操作的字节码。
-
invokedynamic是在 JVM 字节码中引入的,用于不遵循 Java 的方法查找的动态方法查找。为什么 Java 编译器会使用它?虚拟方法分派是使用invokevirtual和invokeinterface完成的。请注意,如果需要,Java 编译器当然可以免费使用invokedynamic。 Java 语言规范没有说明 Java 必须如何编译,甚至根本没有说它必须被编译,它也可以被解释。 -
您正在发布 main() 的字节码,其中进行了 0 个多态方法调用:只有构造函数调用和静态方法调用(到
ride())。多态方法调用在TestRide.ride() -
@mastov 没有。它有 4 次对静态方法ride() 的调用静态调用,以 Cycle 作为参数。
-
基类什么都想不出来。 invokevirtual 具有 Cycle 对象,在该对象上调用ride() 方法作为堆栈上的第一个参数。它获取该 Cycle 对象的 concrete 类(例如,获取 BiCycle 或 TriCycle)。然后它按照docs.oracle.com/javase/specs/jvms/se7/html/… 中描述的过程在该具体类中查找ride() 方法
标签: java jvm polymorphism bytecode