有三种操作码可以调用Java方法。很明显,invokeStatic仅用于静态方法调用。
据我所知,在调用构造函数和私有方法时使用了invokespecial。那么,我们是否需要在运行时区分私有和公共方法调用?可以使用相同的操作码调用invokevirtual吗?
JVM是否处理私有和公共方法定义?据我所知,在封装的开发阶段只需要公共和私有关键字?
答案 0 :(得分:33)
如果仔细阅读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编译器生成的汇编指令的命令:
想象你有一个A.java类
A级 { 公共无效printValue() { System.out.println(“ Inside A”); }
公共静态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指令。公共方法决不意味着每次都会生成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测试