什么时候JVM字节码访问修饰符标志0x1000(十六进制)"合成"设置?

Pet*_*ler 9 java bytecode access-modifiers .class-file

对于某些Java字节代码解析器项目,我阅读了JVM规范,并发现Java虚拟机类文件格式访问修饰符字段的位掩码值是

  ACC_PUBLIC = 0x0001
  ACC_FINAL = 0x0010
  ACC_SUPER = 0x0020 # old invokespecial instruction semantics (Java 1.0x?)
  ACC_INTERFACE = 0x0200
  ACC_ABSTRACT = 0x0400
  ACC_SYNTHETIC = 0x1000 
  ACC_ANNOTATION = 0x2000
  ACC_ENUM = 0x4000
Run Code Online (Sandbox Code Playgroud)

不知怎的,我不知道0x1000是为了什么.我在内部类中看到过一次,但是从那时起我检查了所有内部类,这个标志从未设置过.你现在知道这个标志的含义是什么,设置在何处/何时?

Raf*_*ter 9

合成元素是存在于已编译的类文件中但不在其编译的源代码中的任何元素.通过检查元素是否为合成元素,可以将这些元素区分为可反复处理代码的工具.这当然首先与使用反射的库相关,但它也与IDE之类的其他工具相关,这些工具不允许您调用合成方法或使用合成类.最后,Java编译器在编译期间验证代码永远不会直接使用合成元素也很重要.合成元素仅用于使Java运行时满意,它简单地处理(和验证)交付的代码,其中合成元素与任何其他元素相同.

您已经提到内部类作为Java编译器插入合成元素的示例,所以让我们看看这样一个类:

class Foo {

  private String foo;

  class Bar {

    private Bar() { }

    String bar() {
      return foo;
    }
  }

  Bar bar() {
    return new Bar();
  }
}
Run Code Online (Sandbox Code Playgroud)

这个编译非常精细但没有合成元素,它会被一个不了解内部类的JVM拒绝.Java编译器desugares上面的类的东西像下面这样:

class Foo {

  private String foo;

  String access$100() {  // synthetic method
    return foo;
  }

  Foo$Bar bar() {
    return new Foo$Bar(this, (Foo$1)null);
  }

  Foo() { } // NON-synthetic, but implicit!
}

class Foo$Bar {

  private final Foo $this; // synthetic field

  private Foo$Bar(Foo $this) {  // synthetic parameter
    this.$this = $this;
  }

  Foo$Bar(Foo $this, Foo$1 unused) {  // synthetic constructor
    this($this);
  }

  String bar() {
    return $this.access$100();
  }
}

class Foo$1 { /*empty, no constructor */ } // synthetic class
Run Code Online (Sandbox Code Playgroud)

如上所述,JVM不了解内部类,但强制成员的私有访问,即内部类无法访问其封闭类的私有属性.因此,Java编译器需要向被访问的类添加所谓的访问器,以便公开其不可见的属性:

  1. foo字段是私有的,因此只能从内部访问Foo.该access$100方法将此字段公开给其包,其中始终要找到内部类.此方法是合成的,因为它是由编译器添加的.

  2. Bar构造函数是私有的,因此只能从它自己的类中调用.为了实例化一个实例Bar,另一个(合成)构造函数需要公开实例的构造.但是,构造函数具有固定的名称(在内部,它们都被调用<init>),因此我们不能将该技术应用于我们只是命名它们的方法访问器access$xxx.相反,我们通过创建合成类型使构造函数访问器成为唯一的Foo$1.

  3. 为了访问其外部实例,内部类需要存储对此实例的引用,该引用存储在合成字段中$this.此引用需要通过构造函数中的合成参数传递给内部实例.

合成元素的其他示例是表示lambda表达式的类,使用类型发散签名覆盖方法时的桥接方法,Proxy由Maven构建等其他工具或Byte Buddy等运行时代码生成器创建的类或类的创建(无耻插件) ).