Why java bytecode from a class have come code for new staic inner class appear jvm instruction ACONST_NULL

huangapple go评论73阅读模式
英文:

Why java bytecode from a class have come code for new staic inner class appear jvm instruction ACONST_NULL

问题

我尝试创建一个内部静态类,但我发现在字节码中,在<code>NEW</code>、<code>DUP</code>和<code>INVOKE_SPECIAL</code>之间出现了JVM指令<code>ACONST_NULL</code>,但我知道一个类的新建过程应该是:

  • NEW
  • DUP
  • INVOKE_SPECIAL

为什么会出现下面这段代码:

 L0
    LINENUMBER 13 L0
    NEW com/hoho/api/Main$InnerMain
    DUP
    ACONST_NULL
    INVOKESPECIAL com/hoho/api/Main$InnerMain.&lt;init&gt; (Lcom/hoho/api/Main$1;)V
    ASTORE 1

<code>ACONST_NULL</code>是什么意思?

英文:

I try to new a inner staic class, But I find that bytecode appear the jvm instruction <code>ACONST_NULL</code> bwteen <code>NEW</code>,<code>DUP</code> and <code>INVOKE_SPECIAL</code>,
But I know about a class new is

  • NEW
  • DUP
  • INVOKE_SPECIAL
package com.hoho.api;

/**
 * @author linuxea
 */
public class Main {

    private static class InnerMain {
        // no field
    }

    public static void main(String[] args) {
        InnerMain innerMain = new InnerMain();
    }

}

// class version 52.0 (52)
// access flags 0x21
public class com/hoho/api/Main {

  // compiled from: Main.java
  // access flags 0xA
  private static INNERCLASS com/hoho/api/Main$InnerMain com/hoho/api/Main InnerMain
  // access flags 0x1008
  static synthetic INNERCLASS com/hoho/api/Main$1 null null

  // access flags 0x1
  public &lt;init&gt;()V
   L0
    LINENUMBER 6 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.&lt;init&gt; ()V
    RETURN
   L1
    LOCALVARIABLE this Lcom/hoho/api/Main; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x9
  public static main([Ljava/lang/String;)V
    // parameter  args
   L0
    LINENUMBER 13 L0
    NEW com/hoho/api/Main$InnerMain
    DUP
    ACONST_NULL
    INVOKESPECIAL com/hoho/api/Main$InnerMain.&lt;init&gt; (Lcom/hoho/api/Main$1;)V
    ASTORE 1
   L1
    LINENUMBER 14 L1
    RETURN
   L2
    LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
    LOCALVARIABLE innerMain Lcom/hoho/api/Main$InnerMain; L1 L2 1
    MAXSTACK = 3
    MAXLOCALS = 2
}

Why

 L0
    LINENUMBER 13 L0
    NEW com/hoho/api/Main$InnerMain
    DUP
    ACONST_NULL
    INVOKESPECIAL com/hoho/api/Main$InnerMain.&lt;init&gt; (Lcom/hoho/api/Main$1;)V
    ASTORE 1

What is the <code>ACONST_NULL</code>?

答案1

得分: 5

这是 javac 在 JDK 11 之前解决私有成员访问问题的方式。

InnerMain 被声明为私有,并且它有一个私有的默认构造函数:

private InnerMain() {
}

根据 JVM 规范,任何类都不能访问其他类的私有成员,但根据 Java 语言规则,Main 应该能够访问 InnerMain 的私有成员。为了解决这个问题,javac 生成了一个合成的包私有桥接构造函数。为了确保这个构造函数不能直接从用户代码中调用,编译器在签名中添加了一个虚拟类 Main$1

// 真正的构造函数
private InnerMain() {
}

// 合成的桥接构造函数(包私有,所以 Main 可以调用它)
InnerMain(Main$1 dummy) {
    this();
}

当你写 new InnerMain() 时,编译器实际上调用了这个桥接构造函数。虚拟参数的实际值并不重要,因此它被设置为 null,因此会有 ACONST_NULL 指令:

public static void main(String[] args) {
    InnerMain innerMain = new InnerMain(null);
}

请注意,当内部类是公共的或包私有的时候,不需要桥接方法。

自 JDK 11 开始,引入了一种全新的机制,详见 JEP 181: 基于嵌套的访问控制。现在,如果你使用 JDK 11 或更新版本编译你的 Main 类,MainInnerMain 将成为嵌套关系,将能够在没有桥接方法和合成类的情况下访问彼此的私有成员。

英文:

This is the way how javac solves private member access problem before JDK 11.

InnerMain is declared private, and it has private default constructor:

private InnerMain() {
}

According to the JVM specification, no class may access private members of other classes, but according to Java language rules, Main should be able to access private members of InnerMain. To solve this problem, javac generates a synthetic package private bridge constructor. To ensure this constructor is not called directly from user code, compiler adds a dummy class Main$1 in the signature:

// Real constructor
private InnerMain() {
}


// Synthetic bridge constructor (package private, so Main can call it)
InnerMain(Main$1 dummy) {
    this();
}

When you write new InnerMain(), the compiler actually calls this bridge constructor. The actual value of the dummy argument does not matter, so it's just set to null - hence ACONST_NULL instruction:

public static void main(String[] args) {
    InnerMain innerMain = new InnerMain(null);
}

Note that bridge methods are not needed when the inner class is public or package private.

Since JDK 11, a fundamentally new mechanism was introduced, see JEP 181: Nest-Based Access Control. Now, if you compile your Main class with JDK 11 or newer, Main and InnerMain will become nestmates and will be able to access private members of each other without bridge methods and synthetic classes.

huangapple
  • 本文由 发表于 2020年9月28日 10:08:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/64095091.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定