58

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

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

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

4

3 回答 3

47

这个网站

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

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

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

于 2012-12-07T13:46:48.450 回答
32

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() 时,它必须打印“超类的有趣方法”。如果使用了 invokevirtual,它将打印“子类的有趣方法”。为什么?因为虚拟机会根据对象的实际类,即Subclass,来选择调用interestingMethod()。所以它会使用子类的interestingMethod()。另一方面,使用invokespecial,虚拟机将根据引用的类型选择方法,因此将调用超类版本的interestingMethod()。

于 2012-12-07T14:12:45.017 回答
1

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

首先我要告诉你invokeStatic、invokeSpecial、invokeVirtual、invokeInterface等是编译器在编译后生成的汇编指令。众所周知,编译后得到了.class文件格式,读不出来。但是 java 提供了一个名为"javap"的工具。

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

  1. 成像你有 A.java 类

    类 A { public void printValue() { System.out.println("Inside A"); }

    public static 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 指令。public 方法绝不意味着每次生成 invokeVirtual 指令。如果从 A 的子类调用 super.printValue() 是例外情况。即如果 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 Test

于 2018-10-09T05:34:52.553 回答