首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么Java编译器11使用invokevirtual来调用私有方法?

为什么Java编译器11使用invokevirtual来调用私有方法?
EN

Stack Overflow用户
提问于 2021-04-23 08:02:21
回答 1查看 1.8K关注 0票数 53

当使用OpenJDK 8编译下面的代码时,对foo()的调用是通过invokespecial完成的,但是当使用OpenJDK 11时,会发出invokevirtual

代码语言:javascript
复制
public class Invoke {
  public void call() {
    foo();
  }

  private void foo() {}
}

当使用javap -v -p javac 1.8.0_282时输出:

代码语言:javascript
复制
  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的输出:

代码语言:javascript
复制
  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在私有方法上的目的是允许嵌套类从外部类调用私有方法。所以我尝试了下面的代码:

代码语言:javascript
复制
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的输出中看到invokevirtualfoo()getValueInNested()中都被使用。

代码语言:javascript
复制
  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
代码语言:javascript
复制
  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调用的
EN

回答 1

Stack Overflow用户

发布于 2021-04-26 01:37:00

一个已经很好的答案

我认为在已经提供和接受的答案中添加一些更多的信息是恰当的,尽管这并不是绝对必要的,但它可能有助于扩大理解,因此它符合用户的最佳利益。

基于嵌套的访问控制

在早期版本中,正如@在接受的答案中已经指出的那样,在Java 11之前,编译器需要创建桥接方法,以允许类在这种情况下访问彼此的私有成员。这些可访问性-扩展桥方法在执行上下文中被调用,编译器将代码插入正在运行的程序中。

这样做既增加了部署的应用程序的大小,又增加了复杂性,而且它使理解幕后所发生的事情变得更加困难。

Java 11引入了基于嵌套访问控制的概念.随着嵌套的概念和JVM中相关的访问规则,这允许类和接口相互嵌套。

嵌套类型可以是私有字段、方法和构造函数。

使用更新的反射API,您现在可以查询有关基于嵌套的访问控制功能的信息。

--Java11中的一些新优点

getNestHost()方法用于获取nest主机的名称,isNestmateOf()方法可用于检查类是否是嵌套对象。此外,getNestMembers()方法返回一个嵌套成员数组。

下面是一个通用示例的链接,由Baeldung.com,基于嵌套的访问控制提供,这使IMHO的好处非常突出。

注意,在反汇编的代码中没有编译器生成的桥接方法。此外,内部类现在可以在链接到上面的示例中直接调用outerPrivate()方法。

票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67226178

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档