Java语言到底有没有虚函数?

本文深入探讨了Java中虚函数的概念及其在字节码层面的实现,揭示了Java普通成员函数实际上具备虚函数特性,实现了多态性。通过实例分析与底层字节码查看,解释了Java如何通过invokevirtual指令调用虚函数,强调了Java语言中虚函数的内在机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前几天面试京东的时候,被问到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类中普通成员函数就是虚函数。

<think>嗯,用户问的是“什么是虚函数?”,这个问题看起来是关于面向对象编程中的概念,特别是C++里的。首先,我需要确认用户对虚函数的理解程度,可能他们刚开始学习OOP或者在使用C++时遇到了相关问题。 首先,虚函数的基本定义。虚函数是用于实现多态的,允许子类重写父类的方法。当通过基类指针或引用调用虚函数时,实际调用的是对象的动态类型对应的函数。这可能有点抽象,需要用更易懂的例子来解释。 接下来,要考虑用户可能的背景。如果他们熟悉C++,可能已经知道虚函数的关键字是virtual,以及override和final的用法。但如果是新手,可能需要从基础讲起,比如类的继承、函数重写,然后引出为什么需要虚函数来实现运行时多态。 用户可能没有问到的深层需求是什么?比如,他们可能在调试时遇到了问题,发现调用的函数不是预期的子类实现,或者想了解如何设计可扩展的类结构。这时候解释虚函数的作用就很重要,特别是虚函数表(vtable)和动态绑定的机制。 还需要区分虚函数和非虚函数的区别。比如,普通函数在编译时确定,而虚函数在运行时根据对象类型决定。这样用户能明白使用虚函数的好处和开销,比如运行时多态带来的灵活性,以及虚函数表带来的内存和性能成本。 另外,可能需要提到纯虚函数和抽象类,这样用户知道如何定义接口。同时,C++11之后的override和final关键字有助于提高代码的可读性和安全性,避免常见的错误,比如函数签名不匹配。 实际应用中,虚函数常用于框架和库的设计,比如GUI事件处理、游戏中的实体行为等。这些例子可以帮助用户理解虚函数的实际用途。 最后,总结虚函数的作用、实现机制和使用场景,确保用户有一个全面的理解,并能够应用到实际编程中。同时,注意不要过于深入复杂的实现细节,除非用户明确需要,否则保持解释的简洁和清晰。</think>### 什么是虚函数虚函数(Virtual Function)是 **面向对象编程(OOP)** 中的核心概念,主要用于实现 **多态性(Polymorphism)**。它在C++、Java(通过`@Override`)、C#(通过`virtual`和`override`)等语言中广泛存在。以下是逐步解释: --- #### 1. **基本定义** - **虚函数** 是在基类中声明为 `virtual` 的成员函数,允许派生类(子类)通过 **重写(Override)** 来提供自己的实现。 - 关键作用:通过基类指针或引用调用函数时,程序会根据 **对象的实际类型** 动态决定调用哪个版本的函数。 --- #### 2. **核心特性:动态绑定(运行时多态)** - **非虚函数**:在编译时根据指针/引用的类型确定调用哪个函数(静态绑定)。 - **虚函数**:在运行时根据对象实际类型决定调用哪个函数(动态绑定)。 **示例**: ```cpp class Animal { public: virtual void speak() { // 虚函数 cout << "Animal sound" << endl; } }; class Dog : public Animal { public: void speak() override { // 重写虚函数 cout << "Woof!" << endl; } }; Animal* animal = new Dog(); animal->speak(); // 输出 "Woof!"(动态绑定) ``` --- #### 3. **虚函数表(vtable)** - **底层机制**:编译器为每个包含虚函数的类生成一个虚函数表(vtable),存储该类所有虚函数的地址。 - **对象内存**:每个对象隐含一个指向vtable的指针(vptr),用于运行时查找正确的函数实现。 --- #### 4. **纯虚函数与抽象类** - **纯虚函数**:在基类中声明但没有实现的虚函数(语法:`virtual void func() = 0;`)。 - **抽象类**:包含纯虚函数的类,不能实例化,必须由派生类实现所有纯虚函数。 --- #### 5. **使用场景** - 当需要 **基类定义接口,子类提供不同实现** 时(例如:图形界面控件、游戏角色行为)。 - 当需要通过 **基类指针统一管理不同子类对象** 时(例如:容器存储多种派生类对象)。 --- #### 6. **注意事项** - **性能开销**:虚函数调用需要通过vptr查找vtable,比非虚函数稍慢。 - **C++11新增关键字**: - `override`:显式标记重写的虚函数,避免拼写错误。 - `final`:禁止派生类重写某个虚函数。 --- ### 总结 虚函数通过 **动态绑定** 实现多态性,是面向对象设计中实现灵活性和扩展性的核心工具。其核心价值在于 **用基类接口统一操作不同的子类对象**,同时隐藏具体实现细节。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值