Java中的Default方法
Java8在接口中新引入了一种default方法。目的是保留原先的结构,只将改动加入到接口中。
public interface A {
default void c(){
System.out.println("A.c");
}
}
public interface B {
default void c(){
System.out.println("B.c");
}
}
如果一个类同时实现两个接口,这两个接口中有同样的方法时,编译时会报错。
解决方法如下:
public class whoisc implements A, B {
public void c(){
// A.super.c();//invokespecial
B.super.c();//指明调用B接口中的方法
// 或者复写该方法
}
public static void main(String[] args){
whoisc realc = new whoisc();
realc.c();//invokevirtual
A realcc = new whoisc();
realcc.c();//invokeinterface
}
}
Java每个class文件有一个虚方法表,虚方法表包含指向类中方法的字节码。该表继承父类中的特定类,扩展父类中的新方法。
class superClass{
public void m1(){};
public void m2(){};
}
class thisClass extends superClass{
public void m2(){};
public void m3(){};
}
virtual Method Table
superClass
superClass/m1()
superClass/m2()
thisClass
superClass/m1()
thisClass/m2()
thisClass/m3()
thisClass
的虚方法表继承了superClass
,并且覆写了m2的链接。
JVM会记住虚方法表中各方法的调用位置,以便在子类中优化优化invokevirtual
的调用。
但是对于invokeinterface
,这种调用优化就不起作用了。
如下:
interface thisInterface{
void interfaceM1();
}
class anotherClass extends thisClass implements thisInterface{
public void m4(){};
public void interfaceM1(){};
}
class anotherInterfaceClass implements thisInterface{
public void m5(){};
public void interfaceM1(){};
}
虚方法表中的方法调用位置
anotherClass
superClass/m1()
thisClass/m2()
thisClass/m3()
anotherClass/m4()
thisInterface/interfaceM1()
anotherInterfaceClass
anotherInterfaceClass/m5()
thisInterface/interfaceM1()
此时anotherClass中调用的interfaceM1()方法位置,与anotherInterfaceClass中调用的interfaceM1()方法位置,并不存在继承关系,所以位置的次序也不是顺序增加的。在invokevirtual
中的调用优化在invokeinterface
中也就不适用了。
JVM中的调用指令
invokevirtual
指令用于调用对象的实例方法,根据对象的实际类型进行分派(虚方法分派),这也是 Java 语言中最常见的方法分派方式。invokeinterface
指令用于调用接口方法,它会在运行时搜索一个实现了这个接口方法的对象,找出适合的方法进行调用。invokeinterface
必须在运行时检查方法的对象引用。invokespecial
指令用于调用一些需要特殊处理的实例方法,包括实例初始化方法、私有方法和父类方法。invokestatic
指令用于调用类方法(static
方法)。