为什么来自类的java字节码出现了新的静态内部类的代码出现jvm指令ACONST_NULL

Lin*_*xea 2 java jvm bytecode jvm-bytecode

我尝试新建一个内部静态类,但我发现字节码出现 jvm 指令ACONST_NULLbwteen NEWDUP并且INVOKE_SPECIAL,但我知道一个类 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();
    }

}

Run Code Online (Sandbox Code Playgroud)
// 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 <init>()V
   L0
    LINENUMBER 6 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()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.<init> (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
}

Run Code Online (Sandbox Code Playgroud)

为什么

// 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 <init>()V
   L0
    LINENUMBER 6 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()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.<init> (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
}

Run Code Online (Sandbox Code Playgroud)

是什么ACONST_NULL

apa*_*gin 5

这是javacJDK 11 之前解决私有成员访问问题的方法。

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

private InnerMain() {
}
Run Code Online (Sandbox Code Playgroud)

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

// Real constructor
private InnerMain() {
}


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

当您编写 时new InnerMain(),编译器实际上会调用此桥构造函数。虚拟参数的实际值无关紧要,所以它只是设置为null- 因此ACONST_NULL指令:

public static void main(String[] args) {
    InnerMain innerMain = new InnerMain(null);
}
Run Code Online (Sandbox Code Playgroud)

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

从 JDK 11 开始,引入了一种全新的机制,请参阅JEP 181:基于 Nest 的访问控制。现在,如果您使用 JDK 11 或更高版本编译您的 Main 类,Main并且InnerMain将成为嵌套伙伴,并且将能够在没有桥接方法和合成类的情况下访问彼此的私有成员。

  • 但无论哪种情况,添加虚拟参数都会产生有趣的后果(当参数数量达到最大时);当我们从嵌套类添加调用者时,以前正确接受的私有构造函数可能会被拒绝,因为现在它有“太多参数”。而删除“private”修饰符会使编译器错误消失。值得庆幸的是,现在已经过去了。 (2认同)