Java internal class final semantic implementation, javafinal semantic implementation
This article describes variables of external classes that are often referenced in java internal classes. However, there is no clue on the surface how the variable information is passed to the internal class. This article describes how to implement these semantics in the internal class from the bytecode layer.
Basic Types of local temporary variables
final int x = 10;new Runnable() { @Override public void run() { System.out.println(x); }}.run();
When the internal class bytecode (javap-p-s-c-v) is output, it is shown as follows:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;3: bipush 105: invokevirtual #3 // Method java/io/PrintStream.println:(I)V8: return
It can be seen that this constant value is directly written into the temporary variables of the internal class, that is, it is equivalent to a variable copy.
Local temporary variable reference type
final T t = new T();new Runnable() { @Override public void run() { System.out.println(t); }}.run();
The bytecode is changed to the following:
Final T val $ t; flags: ACC_FINAL, ACC_SYNTHETIC T $1 (T); Signature: (LT;) V // the bytecode 0: aload_0 1: aload_1 2: putfield #1 // Field val $ t: LT; 5: aload_0 6: invokespecial #2 // Method java/lang/Object. "<init>" :() V 9: return // main function bytecode 0: getstatic #3 // Field java/lang/System. out: Ljava/io/PrintStream; 3: aload_0 4: getfield #1 // Field val $ t: LT; 7: invokevirtual #4 // Method java/io/PrintStream. println :( Ljava/lang/Object;) V 10: return
We can see that a constructor with one parameter is automatically generated, and the corresponding tvalue is passed as a parameter to the internal class, and the final semantics is set, that is, it cannot be modified by internal classes.
The above is a non-argument constructor. If it is an internal class with parameters, it is as follows:
Thread thread = new Thread("thread-1") {@Override public void run() {System.out.println(t);}};
The generated bytecode is as follows:
T$1(java.lang.String, T); Signature: (Ljava/lang/String;LT;)V
It can be seen that the compiler will automatically modify the previously called constructor, and change the constructor that only needs one parameter to pass two parameters, at the same time, the corresponding t is passed in.
Reference Field, basic type
int t = 3;private void xx() { new Runnable() { @Override public void run() { System.out.println(t); } }.run();}
The generated bytecode is as follows:
T$1(T); Signature: (LT;)V flags: Code: stack=2, locals=2, args_size=2 0: aload_0 1: aload_1 2: putfield #1 // Field this$0:LT; 5: aload_0 6: invokespecial #2 // Method java/lang/Object."<init>":()V 9: return
There is no constant definition in the internal class as a temporary variable. Why? Because the t object may be modified at any time.
Reference field, reference type
final String t = new String("abc");private void xx() { new Runnable() { @Override public void run() { System.out.println(t); } }.run();}
The generated bytecode is as follows:
Final T this $0; Signature: LT; T $1 (T); // internal class constructor 0: aload_0 1: aload_1 2: putfield #1 // Field this $0: LT; 5: aload_0 6: invokespecial #2 // Method java/lang/Object. "<init>" :() V 9: return
Here, in the constructor of the internal class, this of the external class is passed in directly. Therefore, in the run method of the internal class, for t, two layers of getField will be called directly, you can get the corresponding information. As follows:
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 3: aload_0 4: getfield #1 // Field this$0:LT; 7: getfield #4 // Field T.t:Ljava/lang/String;10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V13: return
Reference type, reference type, static field
static String t = new String("abc");private void xx() {new Runnable() {@Override public void run() {System.out.println(t);}}.run();}
The bytecode is as follows:
Final T this $0; Signature: LT; flags: ACC_FINAL, ACC_SYNTHETIC T $1 (T); Signature: (LT;) V // constructor bytecode 0: aload_0 1: aload_1 2: putfield #1 // Field this $0: LT; 5: aload_0 6: invokespecial #2 // Method java/lang/Object. "<init>" :() V 9: return // run method bytecode 0: getstatic #3 // Field java/lang/System. out: Ljava/io/PrintStream; 3: getstatic #4 // Field T. t: Ljava/lang/String; 6: invokevirtual #5 // Method java/io/PrintStream. println :( Ljava/lang/String;) V 9: return
It can be seen that, even if the static field is referenced, the reference of the external class will be retained in the internal class to achieve the purpose of reference. At the same time, in the run method, because it is a static field, the getField will not be used, but the getStatic will be used to reference the corresponding field.
Summary
In the generation rules of the internal class bytecode, the constructor is modified to PASS Parameters of variables that need to be referenced in the entire internal class. In addition, the constructor is known because it is an internal class and can be modified at will. Certain optimizations can be made for specific scenarios, such as regular quantification (basic types of temporary variables ).
Because there is no special processing for internal classes at the entire JVM layer, these processing methods are all processed at the compilation layer. At the language layer, the generated information is specified. For example, SYNTHETIC semantics.
In the reflection field Member layer, the following methods are defined:
/** * Returns {@code true} if this member was introduced by * the compiler; returns {@code false} otherwise. * * @return true if and only if this member was introduced by * the compiler. * @jls 13.1 The Form of a Binary * @since 1.5 */public boolean isSynthetic();
This information is introduced by the compiler.
Understanding this has some meaning for the entire language layer, but it does not mean that this will not change in the future. Understanding some implementation details will help you to further think about the code implementation layer, it is not limited to the information you have learned before.
Notes for learning Java !!!
If you have any questions or want to obtain learning resources during the learning process, join the Java learning exchange group. Group Number:454297367Let's learn Java together!