When we read the JDK reflection package source code, we will encounter the isSynthetic() method. One of its implementations is Modifier.isSynthetic(getModifiers()), and the principle of other methods is the same. Modifier is a special class for defining modifiers, among which static final int synchronous = 0x00001000 is one of the modifiers.
I try to decorate my custom class / field / method / constructor with synthetic. But they all fail to compile because they don't know the synthetic modifier. So how does synthetic work?
Official definition
I found the definition in the Java language specification:
Any constructs introduced by the compiler that do not have a corresponding construct in the source code must be marked as synthetic ,except for default constructs and the class initialization method.
If there is no corresponding constructor in the source code for any constructor introduced by the compiler, the code must be marked as synthetic (compound), except for the default constructor and class initialization method.
Verification
Based on the above definition, we know that synthetic is generated by compiler, and synthetic means compound and synthetic. So guess if it's used in inner classes. First, we construct an inner class:
public class SyntheticDemo { public static void main(String[] args) { InnerClass innerObject = new InnerClass(); System.out.println("inner: " + innerObject.inner); } private static class InnerClass{ private String inner = "I am inside."; } }
Three files are generated after compilation:
- SyntheticDemo$1.class
- SyntheticDemo$InnerClass.class
- SyntheticDemo.class
Let's decompile these three class files in turn
SyntheticDemo$1.class
javap SyntheticDemo$1.class:
Compiled from "SyntheticDemo.java" class compiler.SyntheticDemo$1 { }
javap -v SyntheticDemo$1.class
Last modified 2019-12-12; size 205 bytes MD5 checksum 942560dec8fdbc3d9b65e31e4b41295a Compiled from "SyntheticDemo.java" class compiler.SyntheticDemo$1 minor version: 0 major version: 52 flags: ACC_SUPER, ACC_SYNTHETIC Constant pool: #1 = Class #7 // compiler/SyntheticDemo$1 #2 = Class #9 // java/lang/Object #3 = Utf8 SourceFile #4 = Utf8 SyntheticDemo.java #5 = Utf8 EnclosingMethod #6 = Class #10 // compiler/SyntheticDemo #7 = Utf8 compiler/SyntheticDemo$1 #8 = Utf8 InnerClasses #9 = Utf8 java/lang/Object #10 = Utf8 compiler/SyntheticDemo { } SourceFile: "SyntheticDemo.java" EnclosingMethod: #6.#0 // compiler.SyntheticDemo InnerClasses: static #1; //class compiler/SyntheticDemo$1
There is nothing in this class, and the flags of this class contain ACC ﹣ synchronous.
SyntheticDemo$InnerClass.class
javap SyntheticDemo$InnerClass.class:
Compiled from "SyntheticDemo.java" class compiler.SyntheticDemo$InnerClass { compiler.SyntheticDemo$InnerClass(compiler.SyntheticDemo$1); static java.lang.String access$100(compiler.SyntheticDemo$InnerClass); }
javap SyntheticDemo$InnerClass.class:
Classfile /D:/idea_workspace/mycode/target/classes/compiler/SyntheticDemo$InnerClass.class Last modified 2019-12-12; size 763 bytes MD5 checksum f13a4310236918575d26a2909aa8507e Compiled from "SyntheticDemo.java" class compiler.SyntheticDemo$InnerClass minor version: 0 major version: 52 flags: ACC_SUPER Constant pool: #1 = Fieldref #5.#26 // compiler/SyntheticDemo$InnerClass.inner:Ljava/lang/String; #2 = Methodref #5.#27 // compiler/SyntheticDemo$InnerClass."<init>":()V #3 = Methodref #6.#27 // java/lang/Object."<init>":()V #4 = String #28 / / I'm inside #5 = Class #30 // compiler/SyntheticDemo$InnerClass #6 = Class #31 // java/lang/Object #7 = Utf8 inner #8 = Utf8 Ljava/lang/String; #9 = Utf8 <init> #10 = Utf8 ()V #11 = Utf8 Code #12 = Utf8 LineNumberTable #13 = Utf8 LocalVariableTable #14 = Utf8 this #15 = Utf8 InnerClass #16 = Utf8 InnerClasses #17 = Utf8 Lcompiler/SyntheticDemo$InnerClass; #18 = Class #32 // compiler/SyntheticDemo$1 #19 = Utf8 (Lcompiler/SyntheticDemo$1;)V #20 = Utf8 x0 #21 = Utf8 Lcompiler/SyntheticDemo$1; #22 = Utf8 access$100 #23 = Utf8 (Lcompiler/SyntheticDemo$InnerClass;)Ljava/lang/String; #24 = Utf8 SourceFile #25 = Utf8 SyntheticDemo.java #26 = NameAndType #7:#8 // inner:Ljava/lang/String; #27 = NameAndType #9:#10 // "<init>":()V #28 = Utf8 I'm inside #29 = Class #33 // compiler/SyntheticDemo #30 = Utf8 compiler/SyntheticDemo$InnerClass #31 = Utf8 java/lang/Object #32 = Utf8 compiler/SyntheticDemo$1 #33 = Utf8 compiler/SyntheticDemo { compiler.SyntheticDemo$InnerClass(compiler.SyntheticDemo$1); descriptor: (Lcompiler/SyntheticDemo$1;)V flags: ACC_SYNTHETIC Code: stack=1, locals=2, args_size=2 0: aload_0 1: invokespecial #2 // Method "<init>":()V 4: return LineNumberTable: line 18: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcompiler/SyntheticDemo$InnerClass; 0 5 1 x0 Lcompiler/SyntheticDemo$1; static java.lang.String access$100(compiler.SyntheticDemo$InnerClass); descriptor: (Lcompiler/SyntheticDemo$InnerClass;)Ljava/lang/String; flags: ACC_STATIC, ACC_SYNTHETIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: getfield #1 // Field inner:Ljava/lang/String; 4: areturn LineNumberTable: line 18: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 x0 Lcompiler/SyntheticDemo$InnerClass; } SourceFile: "SyntheticDemo.java" InnerClasses: static #18; //class compiler/SyntheticDemo$1
SyntheticDemo$InnerClass contains the constructor and access0 method with default access. And the constructor and method are synthetic. The constructor's argument is synthetic demo $1. The logic of access0 is to get private properties of internal classes.
SyntheticDemo.class
Take a look at the last class, javap synthetic demo.class:
Compiled from "SyntheticDemo.java" public class compiler.SyntheticDemo { public compiler.SyntheticDemo(); public static void main(java.lang.String[]); }
javap -v SyntheticDemo.class
Classfile /D:/idea_workspace/mycode/target/classes/compiler/SyntheticDemo.class Last modified 2019-12-12; size 1040 bytes MD5 checksum 7519ad7d2df356217e19642ed0a06ee4 Compiled from "SyntheticDemo.java" public class compiler.SyntheticDemo minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #13.#32 // java/lang/Object."<init>":()V #2 = Class #33 // compiler/SyntheticDemo$InnerClass #3 = Methodref #2.#34 // compiler/SyntheticDemo$InnerClass."<init>":(Lcompiler/SyntheticDemo$1;)V #4 = Fieldref #35.#36 // java/lang/System.out:Ljava/io/PrintStream; #5 = Class #37 // java/lang/StringBuilder #6 = Methodref #5.#32 // java/lang/StringBuilder."<init>":()V #7 = String #38 // inner: #8 = Methodref #5.#39 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #9 = Methodref #2.#40 // compiler/SyntheticDemo$InnerClass.access$100:(Lcompiler/SyntheticDemo$InnerClass;)Ljava/lang/String; #10 = Methodref #5.#41 // java/lang/StringBuilder.toString:()Ljava/lang/String; #11 = Methodref #42.#43 // java/io/PrintStream.println:(Ljava/lang/String;)V #12 = Class #44 // compiler/SyntheticDemo #13 = Class #45 // java/lang/Object #14 = Class #46 // compiler/SyntheticDemo$1 #15 = Utf8 InnerClasses #16 = Utf8 InnerClass #17 = Utf8 <init> #18 = Utf8 ()V #19 = Utf8 Code #20 = Utf8 LineNumberTable #21 = Utf8 LocalVariableTable #22 = Utf8 this #23 = Utf8 Lcompiler/SyntheticDemo; #24 = Utf8 main #25 = Utf8 ([Ljava/lang/String;)V #26 = Utf8 args #27 = Utf8 [Ljava/lang/String; #28 = Utf8 innerObject #29 = Utf8 Lcompiler/SyntheticDemo$InnerClass; #30 = Utf8 SourceFile #31 = Utf8 SyntheticDemo.java #32 = NameAndType #17:#18 // "<init>":()V #33 = Utf8 compiler/SyntheticDemo$InnerClass #34 = NameAndType #17:#47 // "<init>":(Lcompiler/SyntheticDemo$1;)V #35 = Class #48 // java/lang/System #36 = NameAndType #49:#50 // out:Ljava/io/PrintStream; #37 = Utf8 java/lang/StringBuilder #38 = Utf8 inner: #39 = NameAndType #51:#52 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #40 = NameAndType #53:#54 // access$100:(Lcompiler/SyntheticDemo$InnerClass;)Ljava/lang/String; #41 = NameAndType #55:#56 // toString:()Ljava/lang/String; #42 = Class #57 // java/io/PrintStream #43 = NameAndType #58:#59 // println:(Ljava/lang/String;)V #44 = Utf8 compiler/SyntheticDemo #45 = Utf8 java/lang/Object #46 = Utf8 compiler/SyntheticDemo$1 #47 = Utf8 (Lcompiler/SyntheticDemo$1;)V #48 = Utf8 java/lang/System #49 = Utf8 out #50 = Utf8 Ljava/io/PrintStream; #51 = Utf8 append #52 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder; #53 = Utf8 access$100 #54 = Utf8 (Lcompiler/SyntheticDemo$InnerClass;)Ljava/lang/String; #55 = Utf8 toString #56 = Utf8 ()Ljava/lang/String; #57 = Utf8 java/io/PrintStream #58 = Utf8 println #59 = Utf8 (Ljava/lang/String;)V { public compiler.SyntheticDemo(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 10: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcompiler/SyntheticDemo; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=2, args_size=1 0: new #2 // class compiler/SyntheticDemo$InnerClass 3: dup 4: aconst_null 5: invokespecial #3 // Method compiler/SyntheticDemo$InnerClass."<init>":(Lcompiler/SyntheticDemo$1;)V 8: astore_1 9: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 12: new #5 // class java/lang/StringBuilder 15: dup 16: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V 19: ldc #7 // String inner: 21: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 24: aload_1 25: invokestatic #9 // Method compiler/SyntheticDemo$InnerClass.access$100:(Lcompiler/SyntheticDemo$InnerClass;)Ljava/lang/String; 28: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 31: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 34: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 37: return LineNumberTable: line 14: 0 line 15: 9 line 16: 37 LocalVariableTable: Start Length Slot Name Signature 0 38 0 args [Ljava/lang/String; 9 29 1 innerObject Lcompiler/SyntheticDemo$InnerClass; } SourceFile: "SyntheticDemo.java" InnerClasses: static #14; //class compiler/SyntheticDemo$1
Here, all the puzzles have been solved. In the new inner class, the compiler generated constructor is called and a null is passed as the parameter:
5: invokespecial #3 // Method compiler/SyntheticDemo$InnerClass."<init>":(Lcompiler/SyntheticDemo$1;)V 8: astore_1
When obtaining the private property of an internal class, the compiled access0 method called:
invokestatic #9 // Method compiler/SyntheticDemo$InnerClass.access$100:(Lcompiler/SyntheticDemo$InnerClass;)Ljava/lang/String;
Answer
Because the inner class is private, the default constructor will also be private, so the outer class cannot call the default constructor. We know that external classes in Java language can construct and access internal classes. Its implementation is a trick played by the compiler. The compiler generates some constructors, classes and methods to help us connect the private parts of external classes and internal classes. The constructors, methods and classes generated by such compilers are syntactic tags. That is to say, this feature is given by the java compiler, while other languages running on the JVM do not necessarily have this feature. It depends on whether the corresponding compiler also uses some tricks to connect.
If we remove the private keyword of the inner domain of the InnerClass, and then look up the bytecode, we can find that the access0 of the internal class is gone, because the external class can directly access the domain of package access permission of the internal class, of course, there is no need to generate access0 to bridge. Readers can try it on their own.
If we remove the inner class private of InnerClass, or add a non private inner class constructor instead of removing it, then look at the bytecode and find that the SyntheticDemo .class disappears, and the compiler does not generate the package permission constructor with the SyntheticDemo .class parameter, because it is not needed. Readers can experiment on their own.
extend
Anonymous Inner Class
Is synthetic used in anonymous inner classes?
public class SyntheticDemo2 { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { System.out.println("11"); } }).start(); } }
The above code will also generate a synthetic demo2 $1. Class. Let's look at the bytecode:
Classfile /D:/idea_workspace/mycode/target/classes/compiler/SyntheticDemo2$1.class Last modified 2019-12-12; size 503 bytes MD5 checksum 207c9ba26f66c2fbfd5d092c7f9f844e Compiled from "SyntheticDemo2.java" final class compiler.SyntheticDemo2$1 implements java.lang.Runnable minor version: 0 major version: 52 flags: ACC_FINAL, ACC_SUPER Constant pool: #1 = Methodref #3.#19 // java/lang/Object."<init>":()V #2 = Class #20 // compiler/SyntheticDemo2$1 #3 = Class #21 // java/lang/Object #4 = Class #22 // java/lang/Runnable #5 = Utf8 <init> #6 = Utf8 ()V #7 = Utf8 Code #8 = Utf8 LineNumberTable #9 = Utf8 LocalVariableTable #10 = Utf8 this #11 = Utf8 InnerClasses #12 = Utf8 Lcompiler/SyntheticDemo2$1; #13 = Utf8 run #14 = Utf8 SourceFile #15 = Utf8 SyntheticDemo2.java #16 = Utf8 EnclosingMethod #17 = Class #23 // compiler/SyntheticDemo2 #18 = NameAndType #24:#25 // main:([Ljava/lang/String;)V #19 = NameAndType #5:#6 // "<init>":()V #20 = Utf8 compiler/SyntheticDemo2$1 #21 = Utf8 java/lang/Object #22 = Utf8 java/lang/Runnable #23 = Utf8 compiler/SyntheticDemo2 #24 = Utf8 main #25 = Utf8 ([Ljava/lang/String;)V { compiler.SyntheticDemo2$1(); descriptor: ()V flags: Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 13: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcompiler/SyntheticDemo2$1; public void run(); descriptor: ()V flags: ACC_PUBLIC Code: stack=0, locals=1, args_size=1 0: return LineNumberTable: line 17: 0 LocalVariableTable: Start Length Slot Name Signature 0 1 0 this Lcompiler/SyntheticDemo2$1; } SourceFile: "SyntheticDemo2.java" EnclosingMethod: #17.#18 // compiler.SyntheticDemo2.main InnerClasses: static #2; //class compiler/SyntheticDemo2$1
There is no ACC [synchronous] mark in the bytecode, indicating that it is not related to synchronous.
lambda expression
class SyntheticDemo3 { public static void main(String[] args) { new Thread(()->{}).start(); } }
Only one synthetic demo3.class
Its bytecode:
Classfile /D:/idea_workspace/mycode/target/classes/compiler/SyntheticDemo3.class Last modified 2019-12-12; size 1042 bytes MD5 checksum 50722aaf911c908da83df897f5141c05 Compiled from "SyntheticDemo3.java" class compiler.SyntheticDemo3 minor version: 0 major version: 52 flags: ACC_SUPER Constant pool: #1 = Methodref #7.#22 // java/lang/Object."<init>":()V #2 = Class #23 // java/lang/Thread #3 = InvokeDynamic #0:#28 // #0:run:()Ljava/lang/Runnable; #4 = Methodref #2.#29 // java/lang/Thread."<init>":(Ljava/lang/Runnable;)V #5 = Methodref #2.#30 // java/lang/Thread.start:()V #6 = Class #31 // compiler/SyntheticDemo3 #7 = Class #32 // java/lang/Object #8 = Utf8 <init> #9 = Utf8 ()V #10 = Utf8 Code #11 = Utf8 LineNumberTable #12 = Utf8 LocalVariableTable #13 = Utf8 this #14 = Utf8 Lcompiler/SyntheticDemo3; #15 = Utf8 main #16 = Utf8 ([Ljava/lang/String;)V #17 = Utf8 args #18 = Utf8 [Ljava/lang/String; #19 = Utf8 lambda$main$0 #20 = Utf8 SourceFile #21 = Utf8 SyntheticDemo3.java #22 = NameAndType #8:#9 // "<init>":()V #23 = Utf8 java/lang/Thread #24 = Utf8 BootstrapMethods #25 = MethodHandle #6:#33 // invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke /MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; #26 = MethodType #9 // ()V #27 = MethodHandle #6:#34 // invokestatic compiler/SyntheticDemo3.lambda$main$0:()V #28 = NameAndType #35:#36 // run:()Ljava/lang/Runnable; #29 = NameAndType #8:#37 // "<init>":(Ljava/lang/Runnable;)V #30 = NameAndType #38:#9 // start:()V #31 = Utf8 compiler/SyntheticDemo3 #32 = Utf8 java/lang/Object #33 = Methodref #39.#40 // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;L java/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; #34 = Methodref #6.#41 // compiler/SyntheticDemo3.lambda$main$0:()V #35 = Utf8 run #36 = Utf8 ()Ljava/lang/Runnable; #37 = Utf8 (Ljava/lang/Runnable;)V #38 = Utf8 start #39 = Class #42 // java/lang/invoke/LambdaMetafactory #40 = NameAndType #43:#47 // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/l ang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; #41 = NameAndType #19:#9 // lambda$main$0:()V #42 = Utf8 java/lang/invoke/LambdaMetafactory #43 = Utf8 metafactory #44 = Class #49 // java/lang/invoke/MethodHandles$Lookup #45 = Utf8 Lookup #46 = Utf8 InnerClasses #47 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/ lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; #48 = Class #50 // java/lang/invoke/MethodHandles #49 = Utf8 java/lang/invoke/MethodHandles$Lookup #50 = Utf8 java/lang/invoke/MethodHandles { compiler.SyntheticDemo3(); descriptor: ()V flags: Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 8: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcompiler/SyntheticDemo3; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=1, args_size=1 0: new #2 // class java/lang/Thread 3: dup 4: invokedynamic #3, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable; 9: invokespecial #4 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V 12: invokevirtual #5 // Method java/lang/Thread.start:()V 15: return LineNumberTable: line 11: 0 line 12: 15 LocalVariableTable: Start Length Slot Name Signature 0 16 0 args [Ljava/lang/String; } SourceFile: "SyntheticDemo3.java" InnerClasses: public static final #45= #44 of #48; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles BootstrapMethods: 0: #25 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/Method Type;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; Method arguments: #26 ()V #27 invokestatic compiler/SyntheticDemo3.lambda$main$0:()V #26 ()V
lambda expressions are not implemented by inner classes, but by invokedynamic. Have a chance to write an article about invokedynamic.
For more Java articles, please pay attention to the public address: