https://blog.51cto.com/14687333/2537239
相关概念
-
静态类型和实际类型
Animal jack = new Dog()
jack的静态类型为Animal, 实际类型为Dog
-
虚方法和非虚方法
非虚方法:方法在编译器就确定了具体的调用版本,则这个版本在运行时是不可变的,这样的方法称为非虚方法。
如静态方法、私有方法、final方法、实例构造器、父类方法都是非虚方法。
其他方法为虚方法
-
方法调用指令
invokestatic 调用静态方法,解析阶段(类加载的链接)确定唯一方法版本
Invokespecial 调用方法,私有及父类方法, 也是解析阶段确定唯一方法版本
invokevirtual 调用所有虚方法,但是final方法也用这个调用
invokeinterface 调用接口方法
前两条指令在类加载的解析阶段就能把符号引用解析为直接引用。
-
方法重写和方法重载
对重载方法的调用主要看静态类型,静态类型是什么类型,就调用什么类型的参数方法。
举例:
public class DemoApplication { public static void main(String[] args) throws Exception { Est est = new Est(); Animal animal = new Dog(); est.fun((Dog)animal); // 输出dog est.fun(aniaml) // 输出animal est.fun(new Dog()) // 输出dog } } class Est { public void fun(Animal animal) { System.out.println("animal"); } public void fun(Dog dog) { System.out.println("dog"); } }
对重写方法的调用主要看实际类型。实际类型如果实现了该方法则直接调用该方法,如果没有实现,则在继承关系中从低到高搜索有无实现。
方法重写
abstract class Animal {
abstract void say();
}
class Dog extends Animal {
@Override
void say() {
System.out.println("汪汪");
}
}
class Cat extends Animal {
@Override
void say() {
System.out.println("喵喵");
}
public static void main(String[] args) {
Animal dog = new Dog();
Animal cat = new Cat();
dog.say();
cat.say();
}
}
字节码文件:
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: new #5 // class com/example/demo/Dog
3: dup
4: invokespecial #6 // Method com/example/demo/Dog."<init>":()V
7: astore_1
8: new #7 // class com/example/demo/Cat
11: dup
12: invokespecial #8 // Method "<init>":()V
15: astore_2
16: aload_1
17: invokevirtual #9 // Method com/example/demo/Animal.say:()V
20: aload_2
21: invokevirtual #9 // Method com/example/demo/Animal.say:()V
24: return
可以发现调用的都是 invokevirtual #9 // Method com/example/demo/Animal.say:()V ,但是实际上却调用的是不同的方法。
因为,在编译阶段,编译器只知道对象的静态类型,而不知道实际类型,所以在class文件中只能确定要调用父类的方法。但是在执行时却会判断对象的实际类型。如果实际类型实现这个方法,则直接调用,如果没有实现,则按照继承关系从下往上一次检索,只要检索到就调用,如果始终没有检索到,则抛异常
在以上查找的基础上,由于动态分派非常频繁,JVM 运行时会频繁的、反复的去搜索元数据,所以 JVM 使用了一种优化手段,在方法区建立一个虚方法表,使用虚方法表索引来代替元数据查找(invokevirtual的多态查找流程)以提高性能。