JVM 字节码访问修饰符标志 0x1000(十六进制)何时是“合成的”?放?

发布于 2024-12-21 13:07:16 字数 444 浏览 5 评论 0原文

对于某些 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

不知怎的,我不知道 0x1000 的用途。我在内部类中看到过一次,但是对于从那时起我检查过的所有内部类,这个标志从未设置过。您现在知道该标志的含义是什么以及它在何处/何时设置?

For some Java byte code parser project I read the JVM spec and figured out that the bit mask values of the Java virtual machine class file format access modifier fields are

  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

Somehow I have no idea what 0x1000 is for. I saw it once in an inner class, but for all inner classes I checked since then, this flag was never set. Do you now what the meaning of this flag is and where/when it is set?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

青柠芒果 2024-12-28 13:07:16

合成元素是存在于编译的类文件中但不存在于编译它的源代码中的任何元素。通过检查某个元素是否是合成的,您可以区分这些元素以用于反射处理代码的工具。这当然首先与使用反射的库相关,但也与其他工具(例如 IDE)相关,这些工具不允许您调用合成方法或使用合成类。最后,对于 Java 编译器来说,在编译期间验证代码不要直接使用合成元素也很重要。合成元素仅用于使 Java 运行时满意,它只是处理(并验证)交付的代码,其中合成元素与任何其他元素的处理方式相同。

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

class Foo {

  private String foo;

  class Bar {

    private Bar() { }

    String bar() {
      return foo;
    }
  }

  Bar bar() {
    return new Bar();
  }
}

它编译得很好,但如果没有合成元素,它会被一个不知道任何东西的 JVM 拒绝内部类。 Java 编译器将上述类转换为如下内容:

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

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

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

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

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

合成元素的其他示例包括表示 lambda 表达式的类、使用不同类型签名重写方法时的桥接方法、创建 Proxy 类或由 Maven 构建或运行时代码等其他工具创建的类诸如 Byte Buddy(无耻插件)之类的生成器。

A synthetic element is any element that is present in a compiled class file but not in the source code it is compiled from. By checking an element for it being synthetic, you allow a distinction of such elements for tools that process code reflectively. This is of course first of all relevant to libraries that use reflection but it is also relevant for other tools like IDEs that do not allow you to call synthetic methods or to work with synthetic classes. Finally, it is also important for the Java compiler to verify code during its compilation to never directly use synthetic elements. Synthetic elements are only used to make the Java runtime happy which simply processes (and verifies) the delivered code where it treats synthetic elements identically to any other element.

You already mentioned inner classes as an example where synthetic elements are inserted by the Java compiler, so let us look at such a class:

class Foo {

  private String foo;

  class Bar {

    private Bar() { }

    String bar() {
      return foo;
    }
  }

  Bar bar() {
    return new Bar();
  }
}

This compiles perfectly fine but without synthetic elements, it would be refused by a JVM that does not know a thing about inner classes. The Java compiler desugares the above class to something like the following:

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

As said, the JVM does not know about inner classes but enforces private access of members, i.e. an inner class would not be able to access its enclosing classes' private properties. Thus, the Java compiler needs to add so-called accessors to an accessed class in order to expose its non-visible properties:

  1. The foo field is private and can therefore only be accessed from within Foo. The access$100 method exposes this field to its package in which an inner class is always to be found. This method is synthetic as it is added by the compiler.

  2. The Bar constructor is private and can therefore only be called from within its own class. In order to instantiate an instance of Bar, another (synthetic) constructor needs to expose the construction of an instance. However, constructors have a fixed name (internally, they are all called <init>), thus we cannot apply the technique for method accessors where we simply named them access$xxx. Instead, we make constructor accessors unique by creating a synthetic type Foo$1.

  3. In order to access its outer instance, an inner class needs to store a reference to this instance which is stored in a synthetic field $this. This reference needs to be handed to the inner instance by a synthetic parameter in the constructor.

Other examples for synthetic elements are classes that represent lambda expressions, bridge methods when overriding methods with a type-divergent signatures, the creation of Proxy classes or classes that are created by other tools like Maven builds or runtime code generators such as Byte Buddy (shameless plug).

爱她像谁 2024-12-28 13:07:16

它是“合成”标志,在编译器生成字段或方法时设置。 AFAIK 它用于内部类,它与您的观察相一致,并且当工件没有出现在源代码中时必须设置。

http://java.sun.com/docs /books/jvms/second_edition/html/ClassFile.doc.html#88571

It's the "synthetic" flag, set when the field or method is generated by the compiler. AFAIK it's for inner classes, which meshes with your observation, and must be set when an artifact doesn't appear in the source code.

http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#88571

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文