对于某些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
是什么。我在内部类中看到过一次,但是从那时起我检查了所有内部类,这个标志从未设置过。你现在知道这个标志的含义是什么,设置在何处/何时?
答案 0 :(得分:9)
合成元素是编译的类文件中存在的任何元素,但不是编译它的源代码中的元素。通过检查元素是否为合成元素,可以将这些元素区分为可反复处理代码的工具。这当然首先与使用反射的库相关,但它也与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编译器需要向被访问的类添加所谓的访问器,以便公开其不可见的属性:
foo
字段是私有的,因此只能在Foo
内访问。 access$100
方法将此字段公开给其中始终要找到内部类的包。此方法是合成的,因为它是由编译器添加的。
Bar
构造函数是私有的,因此只能在其自己的类中调用。为了实例化Bar
的实例,另一个(合成)构造函数需要公开实例的构造。但是,构造函数具有固定的名称(在内部,它们都被称为<init>
),因此我们不能将该技术应用于我们只是将它们命名为access$xxx
的方法访问器。相反,我们通过创建合成类型Foo$1
来使构造函数访问器成为唯一的。
为了访问其外部实例,内部类需要存储对此实例的引用,该引用存储在合成字段$this
中。此引用需要通过构造函数中的合成参数传递给内部实例。
合成元素的其他示例是表示lambda表达式的类,使用类型 - 发散签名覆盖方法时的桥接方法,由Maven构建或运行时代码等其他工具创建的Proxy
类或类的创建诸如Byte Buddy之类的生成器(无耻插件)。
答案 1 :(得分:3)
它是“synthetic”标志,在编译器生成字段或方法时设置。 AFAIK用于内部类,与您的观察相结合,并且必须在源代码中未出现工件时设置。
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#88571