Java 存在invokeVirtual时为什么需要invokeSpecial

Java 存在invokeVirtual时为什么需要invokeSpecial,java,jvm,bytecode,Java,Jvm,Bytecode,有三个操作码可以调用Java方法。显然,invokeStatic只是用于静态方法调用 据我所知,调用构造函数和私有方法时使用invokespecial。那么,我们需要在运行时区分私有和公共方法调用吗?它可以用相同的操作码调用,比如invokevirtual JVM是否处理私有和公共方法定义?据我所知,在开发阶段封装只需要公共和私有关键字?From 如果仔细阅读Java VM规范,很容易找到答案: invokespecial指令和invokevirtual指令之间的区别在于invokevirtua

有三个操作码可以调用Java方法。显然,invokeStatic只是用于静态方法调用

据我所知,调用构造函数和私有方法时使用invokespecial。那么,我们需要在运行时区分私有和公共方法调用吗?它可以用相同的操作码调用,比如invokevirtual

JVM是否处理私有和公共方法定义?据我所知,在开发阶段封装只需要公共和私有关键字?

From

如果仔细阅读Java VM规范,很容易找到答案:

invokespecial指令和invokevirtual指令之间的区别在于invokevirtual基于对象的类调用方法。invokespecial指令用于调用实例初始化方法以及私有方法和当前类的超类的方法

换句话说,invokespecial用于调用方法而不考虑动态绑定,以便调用特定类的方法版本

上面的链接提供了一些有价值的例子,可以清楚地回答我的问题

class Superclass {

    private void interestingMethod() {
        System.out.println("Superclass's interesting method.");
    }

    void exampleMethod() {
        interestingMethod();
    }
}

class Subclass extends Superclass {

    void interestingMethod() {
        System.out.println("Subclass's interesting method.");
    }

    public static void main(String args[]) {
        Subclass me = new Subclass();
        me.exampleMethod();
    }
}

当您在上面定义的子类中调用main()时,它必须打印“超类的有趣方法”。如果使用invokevirtual,它将打印“子类的有趣方法”。为什么?因为虚拟机将根据对象的实际类(即子类)选择要调用的interestingMethod()。所以它将使用子类的interestingMethod()。另一方面,使用invokespecial,虚拟机将根据引用的类型选择方法,因此将调用超类版本的interestingMethod()

感谢您阅读该解释:如果它有助于您在方法调用期间识别汇编指令的创建,请不要忘记向上投票 这里我解释静态绑定和动态绑定

首先,我要告诉您,invokeStatic、invokeSpecial、invokeVirtual、invokeInterface等是编译器在编译过程后生成的汇编指令。 众所周知,编译后我们得到了一个.class文件格式,我们无法读取它。但是java提供了一个名为“javap”的工具

我们可以使用javap命令读取.class文件汇编指令。默认情况下,我们看不到私有方法程序集指令,因此需要使用-private。以下是查看java编译器生成的汇编指令的命令:

  • 假设您有一个.java类

    甲级 { public void printValue() { System.out.println(“内部A”); }

    公共静态无效调用方法(A) { a、 printValue(); } }

  • 打开cmd prompt并转到包含该java文件A.java的文件夹

  • 运行javaca.java

  • 现在生成了一个.class文件,其中包含汇编指令,但您无法读取它

  • 现在运行javap-ca

  • 您可以看到方法调用的程序集生成-->a.printValue()

  • 如果printValue()方法是私有的,则需要使用javap-c-private

  • 您可以将printValue()设置为private/static/public/private static

  • 还有一件事要记住,第一个编译器检查调用方法的对象。然后找到它的类类型,并在该类中找到该方法(如果可用或不可用)

  • 注意:现在请记住,如果调用方法是静态的,则会生成invokeStatic汇编;如果调用方法是私有的,则会生成invokeSpecial汇编指令;如果调用方法是公共的,则会生成invokeVirtual指令。public方法并不意味着每次生成invokeVirtual指令时。在super.printValue()的情况下,来自A的子类的调用是例外情况。i、 e.如果A是B的父类,并且B包含相同的方法printValue(),则它将生成invokeVirtual(动态),但如果B中的printValue()将super.printValue()作为其第一条语句,则即使A的printValue()是公共的,也会生成invokeStatic

    我们也来试试这个:

    class B extends A
    {
    public void printValue()
    {
    super.printValue();// invokeStatic
    System.out.println("Inside B");
    }
    
    }
    
    public class Test
    {
    public static void main(String[] arr)
    {
        A a = new A();
        B b = new B();
        A.callMethod(a);// invokeVirtual
        A.callMethod(b);// invokeVirtual
    }
    }
    
    -->通过Test.java保存它 -->运行javactest.java
    -->javap-c-private Test

    根据您的评论,在调用private方法时可能需要invokespecial。的可能重复,当然,除非您正在进行超级调用,并且目标类具有ACC_super标志(每个现代类都会)。我想不出有这样的代码的好理由。这是,如果您这样做,任何好的IDE都会给您一个警告或错误。两个
    interestingMethod
    签名之间的细微差别足以让读者对正在发生的事情感到困惑。@wchargin例如,当超类是您扩展的库类时,可能会发生这种情况。也许你甚至没有它的源代码。您当然不关心它的私有方法。很可能您向子类添加了一个方法,该方法恰好与超类中的私有方法同名。上面的例子很好地说明了“什么样的源代码可能会产生所讨论的字节码”,但这并不意味着这是一个好的软件工程。:-)就这一点而言,这样的源代码不必是Java语言,仅仅是一些可以编译成JVM字节码的语言。我认为“如果超类从
    exampleMethod()
    调用
    interestingMethod()
    时是
    invokevirtual
    的话,情况也是一样的。”。但是实际上做了一个实验,通过用
    invokevirtual
    替换
    invokespecial
    来修改
    Superclass.class
    文件中的字节码,运行
    Java子类的输出仍然是“Supe”