为什么invokeVirtual存在时需要invokeSpecial

时间:2012-12-07 13:39:30

标签: java jvm bytecode

有三种操作码可以调用Java方法。很明显,invokeStatic仅用于静态方法调用。

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

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

3 个答案:

答案 0 :(得分:33)

来自this site

  

如果仔细阅读Java VM Spec,可以很容易地找到答案:

     
    

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

  
     

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

答案 1 :(得分:20)

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();
    }
}

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

答案 2 :(得分:0)

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

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

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

  1. 想象你有一个A.java类

    A级 { 公共无效printValue() { System.out.println(“ Inside A”); }

    公共静态void callMethod(A a) {     a.printValue(); } }

  2. 打开cmd提示符,然后转到包含该Java文件A.java的文件夹。

  3. 运行javac A.java。

  4. 现在将生成一个A.class文件,其中包含汇编指令,但您无法阅读。

  5. 现在运行javap -c A

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

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

  8. 您可以将printValue()设置为私有/静态/公共/私有静态。

  9. 要记住的另一件事是,第一个编译器检查要在其上调用该方法的对象。然后找到其类类型,并在该类中找到该方法(如果可用)。

注意:现在请记住,如果我们的调用方法是静态的,则将生成invokeStatic程序集;如果生成其私有的,则会生成invokeSpecial程序集指令;如果生成其公共的,则将会生成invokeVirtual指令。公共方法决不意味着每次都会生成invokeVirtual指令。在super.printValue()的情况下,从A的子类调用是特殊情况。例如,如果A是B的父类,并且B包含相同的方法printValue(),则它将生成invokeVirtual(dynamic),但如果B中的printValue()的第一条语句具有super.printValue(),则即使printValue()的A)是公开的。

我们也尝试一下:

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测试