当使用OpenJDK 8编译下面的代码时,对foo()的调用是通过invokespecial完成的,但是当使用OpenJDK 11时,会发出invokevirtual。
public class Invoke {
public void call() {
foo();
}
private void foo() {}
}当使用javap -v -p javac 1.8.0_282时输出:
public void call();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #2 // Method foo:()V
4: return使用javap -v -p 11.0.10时javac的输出:
public void call();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokevirtual #2 // Method foo:()V
4: return我不明白为什么在这里使用invokevirtual,因为不能覆盖foo()。
在深入研究之后,似乎invokevirtual在私有方法上的目的是允许嵌套类从外部类调用私有方法。所以我尝试了下面的代码:
public class Test{
public static void main(String[] args) {
// Build a Derived such that Derived.getValue()
// somewhat "exists".
System.out.println(new Derived().foo());
}
public static class Base {
public int foo() {
return getValue() + new Nested().getValueInNested();
}
private int getValue() {
return 24;
}
private class Nested {
public int getValueInNested() {
// This is getValue() from Base, but would
// invokevirtual call the version from Derived?
return getValue();
}
}
}
public static class Derived extends Base {
// Let's redefine getValue() to see if it is picked by the
// invokevirtual from getValueInNested().
private int getValue() {
return 100;
}
}
}用11编译这段代码,我们可以在javap的输出中看到invokevirtual在foo()和getValueInNested()中都被使用。
public int foo();
descriptor: ()I
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
0: aload_0
// ** HERE **
1: invokevirtual #2 // Method getValue:()I
4: new #3 // class Test$Base$Nested
7: dup
8: aload_0
9: invokespecial #4 // Method Test$Base$Nested."<init>":(LTest$Base;)V
12: invokevirtual #5 // Method Test$Base$Nested.getValueInNested:()I
15: iadd
16: ireturn public int getValueInNested();
descriptor: ()I
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #1 // Field this$0:LTest$Base;
// ** HERE **
4: invokevirtual #3 // Method Test$Base.getValue:()I
7: ireturn所有这些都有点令人困惑,并提出了一些问题:
invokevirtual来调用私有方法?是否有用例将其替换为invokespecial将是不等效的?getValue()在Nested.getValueInNested()中的调用如何不从Derived中选择方法,因为它是通过invokevirtual调用的发布于 2021-04-26 01:37:00
一个已经很好的答案
我认为在已经提供和接受的答案中添加一些更多的信息是恰当的,尽管这并不是绝对必要的,但它可能有助于扩大理解,因此它符合用户的最佳利益。
基于嵌套的访问控制
在早期版本中,正如@在接受的答案中已经指出的那样,在Java 11之前,编译器需要创建桥接方法,以允许类在这种情况下访问彼此的私有成员。这些可访问性-扩展桥方法在执行上下文中被调用,编译器将代码插入正在运行的程序中。
这样做既增加了部署的应用程序的大小,又增加了复杂性,而且它使理解幕后所发生的事情变得更加困难。
Java 11引入了基于嵌套访问控制的概念.随着嵌套的概念和JVM中相关的访问规则,这允许类和接口相互嵌套。
嵌套类型可以是私有字段、方法和构造函数。
使用更新的反射API,您现在可以查询有关基于嵌套的访问控制功能的信息。
--Java11中的一些新优点
getNestHost()方法用于获取nest主机的名称,isNestmateOf()方法可用于检查类是否是嵌套对象。此外,getNestMembers()方法返回一个嵌套成员数组。
下面是一个通用示例的链接,由Baeldung.com,基于嵌套的访问控制提供,这使IMHO的好处非常突出。
注意,在反汇编的代码中没有编译器生成的桥接方法。此外,内部类现在可以在链接到上面的示例中直接调用outerPrivate()方法。
https://stackoverflow.com/questions/67226178
复制相似问题