方法局部内部 类 中的合成字段如何链接到方法局部变量的值?
How does Synthetic Fields within the Method Local Inner Classes are being linked to the values of the Method Local Variables?
在Java中,只要方法正在执行,方法局部变量就存在于堆栈中,因此对于Method Local Inner 类访问方法局部变量,编译器创建的副本将这些变量放入堆中,这就是为什么这些变量应该是最终的或实际上是最终的:以便存储在堆中的副本保存实际有效值。
所以我知道 Javac 在本地内部 classes 中创建合成字段(源代码中不存在但由编译器创建的字段),并且问题是这实际上是如何工作的,它是否像在局部内部 class 中创建一个字段,其与方法局部变量相同,该变量与该变量具有相同的值,以便内部 class 访问新的创建了一个还是其他东西,除了方法局部内部 classes 的合成字段之外,一般来说合成元素究竟意味着什么?
(我已经阅读 JLS,现在希望找到更清楚的东西?)
代表本地 class 的 class 文件中的合成字段有一个损坏的名称。
javap -c -s
可以用来找出 javac
做了什么。
举下面这个简单的例子class。
class Code {
public static void main(String[] args) throws Throwable {
class MyLocal {
void fn() {
System.err.println(args.length);
}
}
new MyLocal().fn();
}
}
编译后,javap -c -s Code$1MyLocal.class
给出:
class CodeMyLocal {
final java.lang.String[] val$args;
descriptor: [Ljava/lang/String;
CodeMyLocal();
descriptor: ([Ljava/lang/String;)V
Code:
0: aload_0
...
args
的合成字段称为 val$args
。我添加了 -s
选项,所以我们看到 MyLocal
的无参数构造函数在编译时实际上有一个参数。 javap -c Code.class
显示调用该构造函数的字节码:
0: new #7 // class CodeMyLocal
3: dup
4: aload_0
5: invokespecial #9 // Method CodeMyLocal."<init>":([Ljava/lang/String;)V
编译器的工作方式与您自己编译时的工作方式略有不同。如果我们进一步查看本地 class 构造函数的反汇编,我们会发现它在调用 superclass 构造函数之前分配了一个字段。
CodeMyLocal();
descriptor: ([Ljava/lang/String;)V
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field val$args:[Ljava/lang/String;
5: aload_0
6: invokespecial #7 // Method java/lang/Object."<init>":()V
9: return
这相当于:
class MyLocal extends Object {
private final String[] args;
MyLocal(String[] args) {
this.args = args;
super();
}
...
}
不会在 Java 中编译。在 Java 1.4 之前,这没有发生。您最终可能会观察到合成字段的默认值,通常会导致 NullPointerException
.
在Java中,只要方法正在执行,方法局部变量就存在于堆栈中,因此对于Method Local Inner 类访问方法局部变量,编译器创建的副本将这些变量放入堆中,这就是为什么这些变量应该是最终的或实际上是最终的:以便存储在堆中的副本保存实际有效值。
所以我知道 Javac 在本地内部 classes 中创建合成字段(源代码中不存在但由编译器创建的字段),并且问题是这实际上是如何工作的,它是否像在局部内部 class 中创建一个字段,其与方法局部变量相同,该变量与该变量具有相同的值,以便内部 class 访问新的创建了一个还是其他东西,除了方法局部内部 classes 的合成字段之外,一般来说合成元素究竟意味着什么? (我已经阅读 JLS,现在希望找到更清楚的东西?)
代表本地 class 的 class 文件中的合成字段有一个损坏的名称。
javap -c -s
可以用来找出 javac
做了什么。
举下面这个简单的例子class。
class Code {
public static void main(String[] args) throws Throwable {
class MyLocal {
void fn() {
System.err.println(args.length);
}
}
new MyLocal().fn();
}
}
编译后,javap -c -s Code$1MyLocal.class
给出:
class CodeMyLocal {
final java.lang.String[] val$args;
descriptor: [Ljava/lang/String;
CodeMyLocal();
descriptor: ([Ljava/lang/String;)V
Code:
0: aload_0
...
args
的合成字段称为 val$args
。我添加了 -s
选项,所以我们看到 MyLocal
的无参数构造函数在编译时实际上有一个参数。 javap -c Code.class
显示调用该构造函数的字节码:
0: new #7 // class CodeMyLocal
3: dup
4: aload_0
5: invokespecial #9 // Method CodeMyLocal."<init>":([Ljava/lang/String;)V
编译器的工作方式与您自己编译时的工作方式略有不同。如果我们进一步查看本地 class 构造函数的反汇编,我们会发现它在调用 superclass 构造函数之前分配了一个字段。
CodeMyLocal();
descriptor: ([Ljava/lang/String;)V
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field val$args:[Ljava/lang/String;
5: aload_0
6: invokespecial #7 // Method java/lang/Object."<init>":()V
9: return
这相当于:
class MyLocal extends Object {
private final String[] args;
MyLocal(String[] args) {
this.args = args;
super();
}
...
}
不会在 Java 中编译。在 Java 1.4 之前,这没有发生。您最终可能会观察到合成字段的默认值,通常会导致 NullPointerException
.