JVM之Synthetic
Synthetic是人造,合成的意思,在虚拟机很多地方使用ACC_SYNTHETIC表示编译器自动生成的,区别于我们自己写的程序代码。这样说可能比较模糊,我们举个例子:我们创建一个内部类,如下
public class TestInnerClass {
public void say(String msg) {
System.out.println(msg);
}
class InnerClass {
public void sayInner() {
say("hello");
}
}
}
我们用javap看一下内部类InnerClass反编译之后的情况:
完整如下:
Classfile /F:/demo/mallsystem/provider/target/classes/com/mall/test/TestInnerClass$InnerClass.class
Last modified 2024-11-28; size 638 bytes
MD5 checksum 9f5efd7b8d0ad50545773d2a0e198f60
Compiled from "TestInnerClass.java"
class com.mall.test.TestInnerClass$InnerClass
minor version: 0
major version: 52
flags: ACC_SUPER
Constant pool:
#1 = Fieldref #5.#22 // com/mall/test/TestInnerClass$InnerClass.this$0:Lcom/mall/test/TestInnerClass;
#2 = Methodref #6.#23 // java/lang/Object."<init>":()V
#3 = String #24 // hello
#4 = Methodref #25.#26 // com/mall/test/TestInnerClass.say:(Ljava/lang/String;)V
#5 = Class #27 // com/mall/test/TestInnerClass$InnerClass
#6 = Class #28 // java/lang/Object
#7 = Utf8 this$0
#8 = Utf8 Lcom/mall/test/TestInnerClass;
#9 = Utf8 <init>
#10 = Utf8 (Lcom/mall/test/TestInnerClass;)V
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 LocalVariableTable
#14 = Utf8 this
#15 = Utf8 InnerClass
#16 = Utf8 InnerClasses
#17 = Utf8 Lcom/mall/test/TestInnerClass$InnerClass;
#18 = Utf8 sayInner
#19 = Utf8 ()V
#20 = Utf8 SourceFile
#21 = Utf8 TestInnerClass.java
#22 = NameAndType #7:#8 // this$0:Lcom/mall/test/TestInnerClass;
#23 = NameAndType #9:#19 // "<init>":()V
#24 = Utf8 hello
#25 = Class #29 // com/mall/test/TestInnerClass
#26 = NameAndType #30:#31 // say:(Ljava/lang/String;)V
#27 = Utf8 com/mall/test/TestInnerClass$InnerClass
#28 = Utf8 java/lang/Object
#29 = Utf8 com/mall/test/TestInnerClass
#30 = Utf8 say
#31 = Utf8 (Ljava/lang/String;)V
{
final com.mall.test.TestInnerClass this$0;
descriptor: Lcom/mall/test/TestInnerClass;
flags: ACC_FINAL, ACC_SYNTHETIC
com.mall.test.TestInnerClass$InnerClass(com.mall.test.TestInnerClass);
descriptor: (Lcom/mall/test/TestInnerClass;)V
flags:
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:Lcom/mall/test/TestInnerClass;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
LineNumberTable:
line 12: 0
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lcom/mall/test/TestInnerClass$InnerClass;
0 10 1 this$0 Lcom/mall/test/TestInnerClass;
public void sayInner();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: getfield #1 // Field this$0:Lcom/mall/test/TestInnerClass;
4: ldc #3 // String hello
6: invokevirtual #4 // Method com/mall/test/TestInnerClass.say:(Ljava/lang/String;)V
9: return
LineNumberTable:
line 14: 0
line 15: 9
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lcom/mall/test/TestInnerClass$InnerClass;
}
SourceFile: "TestInnerClass.java"
InnerClasses:
#15= #5 of #25; //InnerClass=class com/mall/test/TestInnerClass$InnerClass of class com/mall/test/TestInnerClass
重点看下面标记的,ACC_SYNTHETIC标记出现了
这里编译器在内部类生成了一个TestInnerClass的引用对象this$0,因为是编译器自动合成的,所以标记了ACC_SYNTHETIC。
而下面是内部类的构造方法,可以看到它传入了外部类的实例对象,之后把它赋值给this$0
aload_0默认是实例方法第一个参数是this,即内部类自己的引用
aload_1则是加载第一个入参也就是外部类实例对象
putfield #1就是把外部实例设置给this$0
所以这也就解释了为什么内部类创建需要外部类对象创建了,如果外部类对象还没有生成,怎么传给内部类的构造方法呢?
而我们内部类能调用外部类的方法,也正是通过外部类的实例对象调用的