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

2024-01-20

当使用 OpenJDK 8 中的 Java 编译器编译以下代码时,调用foo()是通过一个完成的invokespecial,但是当使用 OpenJDK 11 时,invokevirtual被发射。

public class Invoke {
  public void call() {
    foo();
  }

  private void foo() {}
}

输出javap -v -p when 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 when javac使用 11.0.10:

  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().

经过一番挖掘后,似乎目的是invokevirtualon private method是允许嵌套类从外部类调用私有方法。所以我尝试了下面的代码:

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 that invokevirtual都用于foo() and in 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

所有这些都有点令人困惑并提出了一些问题:

  • Why invokevirtual是用来调用私有方法的?是否有一个用例将其替换为invokespecial不会是等价的吗?
  • 如何调用getValue() in Nested.getValueInNested()不选择方法Derived因为它被称为通过invokevirtual?

这是作为https://openjdk.java.net/jeps/181 https://openjdk.java.net/jeps/181:基于嵌套的访问控制,以便 JVM 可以允许从嵌套类访问私有方法。

在此更改之前,编译器必须在Base类,嵌套类调用该类。该合成方法将依次调用私有方法Base班级。 Java 11 中的功能增强了 JVM,无需编译器生成合成方法即可实现这一点。

关于是否invokevirtual将调用中的方法Derived类,答案是否定的。私有方法仍然不受约束方法选择 https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-5.html#jvms-5.4.6运行时类(这从未改变):

During execution of an invokeinterface or invokevirtual instruction, a method is selected with respect to (i) the run-time type of the object on the stack, and (ii) a method that was previously resolved by the instruction. The rules to select a method with respect to a class or interface C and a method mR are as follows:

  1. If mR is marked ACC_PRIVATE, then it is the selected method.

EDIT:

根据评论“如果从方法所有者类调用私有方法,仍然使用invokespecial,如果从嵌套类调用私有方法,则使用invokevirtual是否有效?”

正如霍尔格提到的,是的,它是有效的,但基于JEP https://openjdk.java.net/jeps/181,我猜已经做出了切换到的决定invokevirtual为简单起见(但我无法证实这一点,这只是一个猜测):

通过访问规则的更改以及对字节码规则的适当调整,我们可以允许生成调用字节码的简化规则:

  • invokespecial 用于私有嵌套构造函数,
  • invokevirtual 用于私有非接口、nestmate 实例方法,
  • 私有接口的invokeinterface,nestmate实例方法;和
  • invokestatic 用于私有嵌套、静态方法

另一个有趣的注释来自JDK-8197445:JEP 181 的实现:基于嵌套的访问控制 https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8197445:

传统上,invokespecial用于调用private成员们,虽然invokevirtual也有这个能力。而不是扰乱关于超类型的复杂规则invokespecial,我们需要调用private使用不同类中的方法invokevirtual.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么Java编译器11使用invokevirtual来调用私有方法? 的相关文章

  • 如何在java中将数组值排序为循环格式?

    我的数组值如下 String value 1 2 3 4 5 6 7 8 9 10 假设如果我将值 5 传递给 tat 数组 它应该按如下顺序排序 5 6 7 8 9 10 1 2 3 4 怎么办 有人帮忙吗 感谢你 你需要的就是所谓的轮换
  • 如何在 JavaFX 中连接可观察列表?

    我所说的串联是指获得一个新列表 该列表侦听所有串联部分的更改 方法的目的是什么FXCollections concat ObservableList
  • 垃圾收集器如何在幕后工作来收集死对象?

    我正在阅读有关垃圾收集的内容 众所周知 垃圾收集会收集死亡对象并回收内存 我的问题是 Collector 如何知道任何对象已死亡 它使用什么数据结构来跟踪活动对象 我正在研究这个问题 我发现GC实际上会跟踪活动对象 并标记它们 每个未标记的
  • Java 的支持向量机?

    我想用Java编写一个 智能监视器 它可以随时发出警报detects即将到来的性能问题 我的 Java 应用程序正在以结构化格式将数据写入日志文件
  • Android studio - 如何保存先前活动中选择的数据

    这是我的代码片段 这Textview充当按钮并具有Onclicklistner在他们 当cpu1000时Textview单击它会导致cpu g1000其代码如下所示的类 public class Game 1000 extends AppC
  • Android 无法解析日期异常

    当尝试解析发送到我的 Android 客户端的日期字符串时 我得到一个无法解析的日期 这是例外 java text ParseException 无法解析的日期 2018 09 18T00 00 00Z 位于 偏移量 19 在 java t
  • 如何在字段值无效的情况下更改 Struts2 验证错误消息?

    我在 Web 表单上使用 Struts2 验证 如果字段假设为整数或日期 则
  • 如何仅从 Firestore 获取最新更新的数据?

    在 Firestore 上发现任何更改时始终获取整个文档 如何只获取最近更新的数据 这是我的数据 我需要在第一次加载时在聊天中按对象顺序 例如 2018 09 17 30 40 msg和sendby 并且如果数据更新则仅获取新的msg和se
  • 如何使用 JMagick 转换色彩空间?

    如何使用 JMagick API 转换色彩空间 例如 CMYK gt RGB 和 RGB gt CMYK None
  • 如何在.NET中使用java.util.zip.Deflater解压缩放气流?

    之后我有一个转储java util zip Deflater 可以确认它是有效的 因为 Java 的Inflater打开它很好 并且需要在 NET中打开它 byte content ReadSample sampleName var inp
  • 提高 PostgreSQL 1 亿数据左连接查询性能

    我在用Postgresql 9 2 version Windows 7 64 bit RAM 6GB 这是一个Java企业项目 我必须在我的页面中显示订单相关信息 有三个表通过左连接连接在一起 Tables TV HD 389772 行 T
  • 在Java中运行bat文件并等待

    您可能会认为从 Java 启动 bat 文件是一项简单的任务 但事实并非如此 我有一个 bat 文件 它对从文本文件读取的值循环执行一些 sql 命令 它或多或少是这样的 FOR F x in CD listOfThings txt do
  • Java继承,扩展类如何影响实际类

    我正在查看 Sun 认证学习指南 其中有一段描述了最终修饰符 它说 如果程序员可以自由地扩展我们所知的 String 类文明 它可能会崩溃 他什么意思 如果可以扩展 String 类 我是否不会有一个名为 MyString 的类继承所有 S
  • Jetty、websocket、java.lang.RuntimeException:无法加载平台配置器

    我尝试在 Endpoint 中获取 http 会话 我遵循了这个建议https stackoverflow com a 17994303 https stackoverflow com a 17994303 这就是我这样做的原因 publi
  • JDBC 时间戳和日期 GMT 问题

    我有一个 JDBC 日期列 如果我使用 getDate 则会得到 date 仅部分2009 年 10 月 2 日但如果我使用 getTimestamp 我会得到完整的 date 2009 年 10 月 2 日 13 56 78 890 这正
  • 轻松的反应

    我有一个与这里描述的类似的案例 动态更改RESTEasy服务返回类型 https stackoverflow com questions 3786781 dynamically change resteasy service return
  • 将 JScrollPane 添加到 JFrame

    我有一个关于向 Java 框架添加组件的问题 我有一个带有两个按钮的 JPanel 和一个添加了 JTable 的 JScrollPane 我想将这两个添加到 JFrame 中 我可以将 JPanel 添加到 JFrame 或将 JScro
  • Hibernate 本机查询 - char(3) 列

    我在 Oracle 中有一个表 其中列 SC CUR CODE 是 CHAR 3 当我做 Query q2 em createNativeQuery select sc cur code sc amount from sector cost
  • 由 Servlet 容器提供服务的 WebSocket

    上周我研究了 WebSockets 并对如何使用 Java Servlet API 实现服务器端进行了一些思考 我没有花费太多时间 但在使用 Tomcat 进行一些测试时遇到了以下问题 如果不修补容器或至少对 HttpServletResp
  • java'assert'和'if(){}else exit;'之间的区别

    java和java有什么区别assert and if else exit 我可以用吗if else exit代替assert 也许有点谷歌 您应该记住的主要事情是 if else 语句应该用于程序流程控制 而assert 关键字应该仅用于

随机推荐