什么是Java的虚方法呢,我们首先看看什么是虚函数
虚函数
百度百科的解释为:
在某基类中声明为 virtual 并在一个或多个派生类中被重新定义的[成员函数],用法格式为:virtual 函数返回类型 函数名(参数表) {[函数体]};实现[多态性],通过指向派生类的基类[指针]或引用,访问派生类中同名覆盖成员函数。
从上面解释上我们抓住几个关于虚函数的关键字 基类、派生类、同名覆盖(重写),因此我们可以理解为虚函数其实就是描述我们子类重写的父类方法。
在虚函数声明定义这块,C++可以通过virtual关键字来进行直接声明,而在Java中,并没有提供我们关键字来声明虚函数,但是我们通过虚函数的定义,我们可以理解为被override的方法都是virtual的。
关于虚方法的调用
在Java语言中,class文件被会解释成机器码,而方法调用会被解释成具体的方法调用指令,大致可以以下五类指令:
指令 | 描述 |
---|---|
invokestatic | 调用静态方法 |
invokespecial | 调用实例构造方法,私有方法和父类方法 |
invokevirtual | 调用虚方法 |
invokeinterface | 调用接口方法,在运行时再确定一个实现此接口的对象 |
invokedynamic | 在运行时动态解析出调用点限定符所引用的方法之后,调用该方法; |
注意:invokedynamic 指令是jdk1.7才加入的,但是在jdk1.7中并没有开始使用。在jdk1.8中才开始大量使用,主要就是我们大量用的 lambda 表达式。
我们接下来去字节码验证下invokevirtual指令,so! 我们来举个🌰
// 类声明文件如下:
public class VirtualDemo {
public static void main(String[] args) {
Parent obj = new Child();
obj.func();
}
}
class Parent{
public void func(){
System.out.println("parent func");
}
}
class Child extends Parent{
@Override
public void func() {
System.out.println("child func");
}
}
接下来我们来获取这个类的字节码:
// dos 命令如下
javac VirtualDemo.java
javap -verbose VirtualDemo
VirtualDemo部分字节码指令如下:
解释:在字节码第4行,我们可以看到是一个invokespecial指令,代表调用的是调用子类Child的实例构造init方法。在第9行中,我们可以看到是一个invokevirtual指令,表示调用虚方法,并且表示这是父类Parent中的方法func(),但这只是编译时是这样表示,等字节码真正执行的时候会通过方法表去正确找到子类Child中func()方法。
参考链接: