定义三个类
class Father{//父类
//父类的静态方法
public static void showStatic(){
System.out.println("father..static");
}
//父类的final方法
public final void showFinal(){
System.out.println("father..final");
}
//父类的普通方法
public void showFather(){
System.out.println("father..show");
}
}
interface Api{//定义一个接口
public void apiShow();
}
class Son extends Father{//子类
public static void showStatic(){
System.out.println("son..static");
}
private void showPrivate(){
System.out.println("son..private");
}
public void showCommon(){
System.out.println("son..common");
}
public void show(){//测试的主要方法
//invokestatic
showStatic();
// invokevirtual
showFinal();
//invokespecial
super.showFinal();
//invokevirtual
showFather();;
//invokespecial
super.showFather();
//invokespecial
showPrivate();
//invokevirtual
showCommon();
Api api = null;
//invokeinterface
api.apiShow();
}
}
反编译后查看show方法的字节码指令
可以看到只要父类的方法、静态方法、私有方法等,能在编译期就确定下来的就都称为非虚方法 invokestatic,invokespecial
其中子类直接调用showFinal在字节码指令中显示的是invokevirtual,因为在父类中也有一个showFinal方法,编译器确定不了调用的是子类重写的方法还是父类重写的方法,所以属于编译期确定不下来的方法,即虚方法;而子类用super.showFinal在字节码指令中显示的是invokespecial,因为此时已经明用super关键字明确指定是要调用父类中的showFinal方法了,所以这属于编译期能够确定下来的方法,即非虚方法
而api.apiShow在字节码中显示的是invokeinterface 虚方法也是非常容易理解的,Api类是一个接口,大家都知道,接口是可以被多个类实现的,编译器并不知道调的是哪个实现类的方法,自然而然就是属于编译期确定不下来的方法,即虚方法了