有三个操作码可以调用 Java 方法。很明显,invokeStatic 仅用于静态方法调用。
据我所知,在调用构造函数和私有方法时使用了 invokespecial 。那么,我们是否需要在运行时区分私有和公共方法调用?它可以用相同的操作码调用,比如invokevirtual?
JVM 是否处理私有和公共方法定义?据我所知,在开发阶段只需要公共和私有关键字进行封装?
从这个网站
如果仔细阅读 Java VM Spec 很容易找到答案:
invokespecial 和invokevirtual 指令的区别在于invokevirtual 根据对象的类调用方法。invokespecial 指令用于调用实例初始化方法以及私有方法和当前类的超类的方法。
换句话说,invokespecial 用于调用方法而不考虑动态绑定,以便调用特定类的方法版本。
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()。
感谢您阅读该解释:如果它可以帮助您在方法调用期间识别汇编指令的创建,请不要忘记投票。这里我将解释静态与动态绑定。
首先我要告诉你invokeStatic、invokeSpecial、invokeVirtual、invokeInterface等是编译器在编译后生成的汇编指令。众所周知,编译后得到了.class文件格式,读不出来。但是 java 提供了一个名为"javap"的工具。
我们可以使用 javap 命令读出我们的 .class 文件汇编指令。默认情况下,我们看不到私有方法汇编指令,因此我们需要使用 -private 。以下是查看 java 编译器生成的汇编指令的命令:
成像你有 A.java 类
类 A { public void printValue() { System.out.println("Inside A"); }
public static void callMethod(A a) { a.printValue(); } }
打开 cmd 提示符并转到包含该 java 文件 A.java 的文件夹。
运行 javac A.java。
现在生成了包含汇编指令但您无法阅读的 A.class 文件。
现在运行 javap -c A
您可以看到方法调用的程序集生成 --> a.printValue();
如果 printValue( ) 方法是私有的,你需要使用 javap -c -private A 。
您可以将您的 printValue() 设为私有/静态/公共/私有静态。
还有一件事要记住,第一个编译器检查调用方法的对象。然后找到它的类类型,并在该类中找到该方法(如果可用或不可用)。
注意:现在请记住,如果我们的调用方法是静态的,则生成 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