jvm 当invokeVirtual存在时,为什么需要invokeSpecial

vpfxa7rd  于 2023-04-20  发布在  其他
关注(0)|答案(3)|浏览(122)

有三种操作码可以调用Java方法。很明显,invokeStatic只是用于静态方法调用。
据我所知,invokespecial在调用构造函数和私有方法时使用。那么,我们需要在运行时区分私有和公共方法调用吗?它可以用相同的操作码调用,比如invokevirtual?
JVM是否处理私有和公共方法定义?据我所知,公共和私有关键字只是在开发阶段需要封装?

svujldwt

svujldwt1#

this site
如果仔细阅读Java VM规范,可以很容易地找到答案:
invokespecial和invokevirtual指令之间的区别在于invokevirtual基于对象的类调用方法。invokespecial指令用于调用示例初始化方法以及私有方法和当前类的超类的方法。
换句话说,invokespecial用于调用方法而不考虑动态绑定,以便调用特定类的方法版本。
在几种情况下,分派方法调用“而不考虑动态绑定”是很重要的:
首先,从一个构造函数链接到超类构造函数时。
第二,就像在super.foo()中一样,当从一个方法调用一个超类的实现时,如果这个类或它的任何子类覆盖了那个方法,那么 invokevirtual 就会转到错误的地方。
第三,当一个类想要选择使用哪个default版本的方法时,如

interface I { void f(); }
interface J extends I { default void f() { ... } }
interface K extends I { default void f() { ... } }
class C implements J, K {
  @Override public void f() { K.super.f(); }
}

上面的class C有一个菱形继承问题,所以它需要选择调用哪个默认方法。invokespecial 允许它分派到K的版本,但是如果K.super.f通过 invokevirtual 分派,调用将返回到C.f

1l5u6lss

1l5u6lss2#

http://www.artima.com/underthehood/invocationP.html上面的链接给出了有价值的例子,清楚地解决了我的问题。

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时()在上面定义的Subclass中,它必须打印“Superclass的interesting method”。如果使用invokevirtual,它将打印“Subclass的interesting method”。为什么?因为虚拟机将选择interestingMethod()根据对象的实际类来调用,也就是Subclass,所以它将使用Subclass的interestingMethod另一方面,使用invokespecial,虚拟机将根据引用的类型选择方法,因此将调用Superclass版本的interestingMethod()。

w80xi6nr

w80xi6nr3#

谢谢你阅读到这个解释:如果它能帮助你在方法调用期间识别汇编指令的创建,请不要忘记投赞成票。
首先我要告诉大家的是,invokeStatic,invokeSpecial,invokeVirtual,invokeInterface等都是编译器编译后生成的汇编指令,大家都知道编译后得到的是.class文件格式,我们无法读取,但是java提供了一个工具,名为**“javap”**。
我们可以使用javap命令读取我们的.class文件汇编指令。默认情况下,我们看不到私有方法汇编指令,所以我们需要使用-private。下面是查看java编译器生成的汇编指令的命令:
1.想象你有A.java类
public void printValue(){ System.out.println(“A内部”);}
public static void callMethod(A a){ a.printValue();{\fnSimHei\bord1\shad1\pos(200,288)}
1.打开cmd提示符并转到包含Java文件A.java的文件夹。
1.运行javac A.java。
1.现在生成了一个. class文件,其中包含汇编指令,但您无法读取它。
1.现在运行javap -c A
1.您可以看到方法调用的程序集生成--〉a.printValue();
1.如果printValue()方法是private,则需要使用javap -c -private A。
1.你可以将printValue()设置为private / static / public / private static。
1.还有一件事要记住,首先编译器检查方法被调用的对象,然后找到它的类类型,并在该类中找到该方法是否可用。

**注意:**现在请记住,如果我们的调用方法是静态的,则生成invokeStatic汇编,如果它的私有的,则生成invokeSpecial汇编指令,如果它的公共的,则生成invokeVirtual指令。公共方法并不意味着每次都生成invokeVirtual指令。在super.printValue()的情况下,从A的子类调用是例外情况。即,如果A是B的父类,B包含相同的方法printValue(),则它将生成invokeVirtual(dynamic),但如果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保存--〉运行javac Test.java --〉javap -c -private Test

相关问题