https://blog.csdn.net/wuqiuping695/article/details/49069779
前几天面试京东的时候,被问到c++与java函数的区别。其中有一条是,java中有没有虚函数?本文将从虚函数的定义及java字节码层面上告诉你答案。
一、虚函数的定义
定义:在某基类中声明为 virtual 并在一个或多个派生类中被重新定 义的成员函数,用法格式为:virtual 函数返回类型 函数名(参数表) {函数体};实现多态性,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数。----------(百度百科)
举个c++中虚函数的例子:
- #include<iostream>
- using namespace std;
- class A
- {
- public:
- void FUN()
- {
- cout<<"FUN in A is called"<<endl;
- }
- };
- class B : public A
- {
- public:
- void FUN()
- {
- cout<<"FUN in B is called"<<endl;
- }
- };
- int main()
- {
- A a;
- B b;
- a.FUN();
- b.FUN();
- return 0;
- }
FUN in A is called
FUN in B is called
代码小改一下:
- #include<iostream>
- using namespace std;
- class A
- {
- public:
- void virtual FUN()
- {
- cout<<"FUN in A is called"<<endl;
- }
- };
- class B : public A
- {
- public:
- void FUN()
- {
- cout<<"FUN in B is called"<<endl;
- }
- };
- int main()
- {
- A a;
- B b;
- A *p;
- p = &a;
- p->FUN();
- p = &b;
- b.FUN();
- return 0;
- }
同样可以得到运行结果:
FUN in A is called
FUN in B is called
读者可以试试将第6行的关键字virtual去掉试运行下,会发现,运行结果如下:
FUN in A is called
FUN in A is called
通过这个例子,可以看到虚函数的作用------实现多态。指向基类的指针在操作它的多态类对象时,会根据不同的类对象,调用其相应的函数,这个函数就是虚函数。
再来看看java语言,
- package test;
- /**
- * Created by ping on 2015/10/12.
- */
- class A{
- public void FUN(){
- System.out.println("FUN in A is called");
- }
- }
- class B extends A{
- public void FUN(){
- System.out.println("FUN in B is called");
- }
- }
- public class VirtualTest {
- public static void main(String args[]) {
- A a = new A();
- B b = new B();
- A p;
- p = a;
- p.FUN();
- p = b;
- p.FUN();
- }
- }
在上面的代码中,我们同样定义了一个基类指针(在java中应该叫引用)去指向不同的对象,可以发现同样可以实现多态。 也就是说,java的普通成员函数(没有被static、native等关键字修饰)就是虚函数,原因很简单,它本身就实现虚函数实现的功能------多态。
下面,我们从更底层的方面来讲述java中虚函数。
二、java字节码
还是之前的代码,我们用javac编译之后,编译器会生成相应的class字节码文件。通过java自带字节码查看器查看
javap -verbose VirtualTest
上面字节码中,有许多指令,如aload_2、invokespecial、invokevirtual(注:aload指令是用来将数据加载到栈,invokespecial用来调用类的构造函数,invokevirtual用来调用普通函数)。
可以发现,对于A.FUN的调用,java虚拟机所采用的调用方式就是invokevirtual。这些字节码的调用指令,是由《java虚拟机规范》规定的,在制定规范之初,大牛们就直接用invokevirtual来命名调用普通函数的指令。从字节码指令的命名上也可以看出,java中的普通成员函数就是虚函数。
从网上有看到这样一种说法,大家可以作为一个参考。
C++ Java
虚函数 -------- 普通函数纯虚函数 -------- 抽象函数
抽象类 -------- 抽象类
虚基类 -------- 接口
结论:java类中普通成员函数就是虚函数。