jvm是怎样调用方法的
jvm内部有五种调用方法的指令
invokeinterface 用以调用接口方法,在运行时搜索一个实现了这个接口方法的对象,找出适合的方法进行调用。(Invoke interface method) invokevirtual 指令用于调用对象的实例方法,根据对象的实际类型进行分派(Invoke instance method; dispatch based on class) invokestatic 用以调用类方法(Invoke a class (static) method ) invokespecial 指令用于调用一些需要特殊处理的实例方法,包括实例初始化方法、私有方法和父类方法。(Invoke instance method; special handling for superclass, private, and instance initialization method invocations ) invokedynamic JDK1.7新加入的一个虚拟机指令,相比于之前的四条指令,他们的分派逻辑都是固化在JVM内部,而invokedynamic则用于处理新的方法分派:它允许应用级别的代码来确定执行哪一个方 |
invokestatic和invokespecial是在编译器确定的,他们适用于不会发生重写的情况
invokevirtual和invokeinterface 是虚方法,这两个指令在调用时,通过一张方法表来调用。
看两个类
public class Animal { protected void sleep(){ System.out.println("动物能睡觉"); } } public class Bird extends Animal implements Fly { @Override protected void sleep() { System.out.println("鸟会睡觉"); } @Override public void fly() { System.out.println("鸟会飞"); } public static void print() { System.out.println("这是鸟"); } } |
两个类对应的方法表
animal的方法表
0 |
sleep |
---|
bird方法表
0 | sleep |
---|---|
1 | fly |
如果重写父类的方法,父类方法的index和子类的一致,方便查找。
由上可知,使用继承或实现会让程序变慢,因为需要查方法表,但是没有必要因为慢而放弃面向对象良好的设计,因为这个时间很短并且jvm还有针对虚方法的优化,内联缓存。
内联缓存举个例子来说
Animal animal = new Bird();
animal.sleep();
这个时候jvm缓存下bird对象和sleep方法,下次bird对象想要调用sleep方法时,缓存里存了bird的方法就不用去查表了。