Preface
Preface To better consolidate and comb JVM knowledge, write this article. Prepare for the fall enrollment. Most of this knowledge comes from Understanding JAVA Virtual Machines. Interested people can read it by themselves~! There is also some content from the B-Station decryption JVM [Produced by the Black Horse Programmer] teaching video Tip: The following is the main content of this article. The following cases can be used as reference tips: All virtual machines for JVM articles are HotSpot virtual machinesClass I File Structure
This part is a simple comb for me, but personally not the first choice to master
Byte code file
Class files are essentially a binary stream based on 8-bit bytes, with each data item arranged in a compact, strictly sequential order in the class file. The jvm parses the binary data according to its specific rules to get relevant information.
0000000 ca fe ba be 00 00 00 34 00 23 0a 00 06 00 15 09 0000020 00 16 00 17 08 00 18 0a 00 19 00 1a 07 00 1b 07 0000040 00 1c 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29 0000060 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e 0000100 75 6d 62 65 72 54 61 62 6c 65 01 00 12 4c 6f 63 0000120 61 6c 56 61 72 69 61 62 6c 65 54 61 62 6c 65 01 0000140 00 04 74 68 69 73 01 00 1d 4c 63 6e 2f 69 74 63 0000160 61 73 74 2f 6a 76 6d 2f 74 35 2f 48 65 6c 6c 6f 0000200 57 6f 72 6c 64 3b 01 00 04 6d 61 69 6e 01 00 16 0000220 28 5b 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 0000240 69 6e 67 3b 29 56 01 00 04 61 72 67 73 01 00 13 0000260 5b 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 0000300 6e 67 3b 01 00 10 4d 65 74 68 6f 64 50 61 72 61 0000320 6d 65 74 65 72 73 01 00 0a 53 6f 75 72 63 65 46 0000340 69 6c 65 01 00 0f 48 65 6c 6c 6f 57 6f 72 6c 64 0000360 2e 6a 61 76 61 0c 00 07 00 08 07 00 1d 0c 00 1e 0000400 00 1f 01 00 0b 68 65 6c 6c 6f 20 77 6f 72 6c 64 0000420 07 00 20 0c 00 21 00 22 01 00 1b 63 6e 2f 69 74 0000440 63 61 73 74 2f 6a 76 6d 2f 74 35 2f 48 65 6c 6c 0000460 6f 57 6f 72 6c 64 01 00 10 6a 61 76 61 2f 6c 61 0000500 6e 67 2f 4f 62 6a 65 63 74 01 00 10 6a 61 76 61 0000520 2f 6c 61 6e 67 2f 53 79 73 74 65 6d 01 00 03 6f 0000540 75 74 01 00 15 4c 6a 61 76 61 2f 69 6f 2f 50 72 0000560 69 6e 74 53 74 72 65 61 6d 3b 01 00 13 6a 61 76 0000600 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65 61 6d 0000620 01 00 07 70 72 69 6e 74 6c 6e 01 00 15 28 4c 6a 0000640 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 0000660 29 56 00 21 00 05 00 06 00 00 00 00 00 02 00 01 0000700 00 07 00 08 00 01 00 09 00 00 00 2f 00 01 00 01 0000720 00 00 00 05 2a b7 00 01 b1 00 00 00 02 00 0a 00 0000740 00 00 06 00 01 00 00 00 04 00 0b 00 00 00 0c 00 0000760 01 00 00 00 05 00 0c 00 0d 00 00 00 09 00 0e 00 0001000 0f 00 02 00 09 00 00 00 37 00 02 00 01 00 00 00 0001020 09 b2 00 02 12 03 b6 00 04 b1 00 00 00 02 00 0a 0001040 00 00 00 0a 00 02 00 00 00 06 00 08 00 07 00 0b 0001060 00 00 00 0c 00 01 00 00 00 09 00 10 00 11 00 00 0001100 00 12 00 00 00 05 01 00 10 00 00 00 01 00 13 00 0001120 00 00 02 00 14
According to the JVM specification, the class file structure is as follows
u4 magic u2 minor_version; u2 major_version; u2 constant_pool_count; cp_info constant_pool[constant_pool_count-1]; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces[interfaces_count]; u2 fields_count; field_info fields[fields_count]; u2 methods_count; method_info methods[methods_count]; u2 attributes_count; attribute_info attributes[attributes_count];
I won't go into it here. Interested can own Baidu.
Decompiled byte code file
From the example above, it is found that the byte code is not understandable or difficult to understand. Java provides us with a tool that Java built-in decompile tool, javap, can decompile byte code files.
Enter the command javap-verbose-p Main. Class View output:
For instance:
class A { private static final int _4MB = 4 * 1024 * 1024; public static void main(String[] args) { int a = 10; int b = Short.MAX_VALUE + 1; int c = a + b; System.out.println(c); } }
Byte Code Decompilation
E:\workspace\leetcode\src\Leetcode\Leetcode>javap -v Leet.class Classfile /E:/workspace/leetcode/src/Leetcode/Leetcode/Leet.class Last modified 2021 August 11, 2001; size 472 bytes MD5 checksum fdd95ab7d3da9a643886467c3404c707 Compiled from "Leet.java" class Leetcode.Leet minor version: 0 major version: 55 flags: (0x0020) ACC_SUPER this_class: #6 // Leetcode/Leet super_class: #7 // java/lang/Object interfaces: 0, fields: 0, methods: 2, attributes: 1 Constant pool: #1 = Methodref #7.#18 // java/lang/Object."<init>":()V #2 = Class #19 // java/lang/Short #3 = Integer 32768 #4 = Fieldref #20.#21 // java/lang/System.out:Ljava/io/PrintStream; #5 = Methodref #22.#23 // java/io/PrintStream.println:(I)V #6 = Class #24 // Leetcode/Leet #7 = Class #25 // java/lang/Object #8 = Utf8 <init> #9 = Utf8 ()V #10 = Utf8 Code #11 = Utf8 LineNumberTable #12 = Utf8 main #13 = Utf8 ([Ljava/lang/String;)V #14 = Utf8 MethodParameters #15 = Utf8 args #16 = Utf8 SourceFile #17 = Utf8 Leet.java #18 = NameAndType #8:#9 // "<init>":()V #19 = Utf8 java/lang/Short #20 = Class #26 // java/lang/System #21 = NameAndType #27:#28 // out:Ljava/io/PrintStream; #22 = Class #29 // java/io/PrintStream #23 = NameAndType #30:#31 // println:(I)V #24 = Utf8 Leetcode/Leet #25 = Utf8 java/lang/Object #26 = Utf8 java/lang/System #27 = Utf8 out #28 = Utf8 Ljava/io/PrintStream; #29 = Utf8 java/io/PrintStream #30 = Utf8 println #31 = Utf8 (I)V { Leetcode.Leet(); descriptor: ()V flags: (0x0000) Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 4: 0 public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=4, args_size=1 0: bipush 10 2: istore_1 3: ldc #3 // int 32768 5: istore_2 6: iload_1 7: iload_2 8: iadd 9: istore_3 10: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 13: iload_3 14: invokevirtual #5 // Method java/io/PrintStream.println:(I)V 17: return LineNumberTable: line 6: 0 line 7: 3 line 8: 6 line 9: 10 line 11: 17 MethodParameters: Name Flags args } SourceFile: "Leet.java"
1) Constant pool loads runtime memory
Constant pools are also method areas, but they are presented separately here
If the data is less than short (32767), it is not stored in the constant pool directly after the byte code
2) Method Byte Code Loading Method Area
(stack=2, locals=4) corresponds to two spaces (four bytes per space) in the operand stack and four slots in the local variable table
3) The main thread starts running, allocating stack frame memory
(stack=2, locals=4) corresponds to two spaces (four bytes per space) in the operand stack and four slots in the local variable table
(Local variable table in green, operand stack in light blue (4 bytes))
4) Execution engine starts executing byte code
bipush 10
-
Push a byte into the operand stack
Similar instructions include
- sipush pushes a short into the operand stack (its length complements four bytes)
- ldc pushes an int into the operand stack
- ldc2_w pushes a long into the operand stack (twice because long is eight bytes)
- Here small numbers exist along with byte code instructions, and numbers beyond the short range are stored in the constant pool
istore 1
Pop up the top element of the operand stack into slot 1 of the local variable table
a = 10 in the corresponding code
ldc #3
Read #3 in the runtime constant pool, that is, 32768 (the number exceeding the short maximum is placed in the runtime constant pool), and load it into the operand stack
Note Short.MAX_VALUE is 32767, so 32768 = Short.MAX_VALUE + 1 is actually calculated during compilation
istore 2
Pop out the element in the operand stack and place it at position 2 of the local variable table
iload1 iload2
Place elements at positions 1 and 2 in the local variable table on the operand stack
- Because operations can only be performed on the operand stack
iadd
Two elements in the operand stack pop out of the stack and add up, resulting in a push into the operand stack
istore 3
Pop out elements from the operand stack and place them in position 3 of the local variable table
getstatic #4
Found #4 in the runtime constant pool and found an object
Find the object in heap memory and place its reference in the operand stack
iload 3
Push the element at position 3 in the local variable table into the operand stack
invokevirtual 5
Locate the constant pool #5 item and navigate to the method area java/io/PrintStream.println:(I)V method
Generate a new stack frame (assign locals, stacks, etc.)
Pass parameter, execute byte code in new stack frame
Execution complete, pop up stack frame
Clear main operand stack contents
return
Complete main method call, pop up main stack frame, end of program
5) Conditional Judgment Instruction
Exercise: Analyse why x=0
class Leet { public static void main(String[] args) { int i = 0; int x = 0; while (i < 10) { x = x++; i ++; } System.out.println(x); } }
You can easily see the problem by looking at the byte code:
Code: stack=2, locals=3, args_size=1 //Operand stack allocates 2 spaces, local variable table allocates 3 spaces 0: iconst_0 //Prepare a constant of 0 1: istore_1 //Place constant 0 in slot 1 of the local variable table i=0 2: iconst_0 //Prepare a constant of 0 3: istore_2 //Place constant 0 in slot 2 of a local variable x=0 4: iload_1 //Place the number of slot 1 of the local variable in the operand stack 5: bipush 10 //Put the number 10 in the operand stack, where there are two numbers in the operand stack 7: if_icmpge 21 //Compare two numbers in the operand stack and jump to 21 if the number below is greater than the number above. The comparison here is to subtract two numbers. Because operations are involved, two numbers pop up the operand stack to perform operations. Empty operand stack after operation 10: iload_2 //Place the number of slot 2 of the local variable in the operand stack, with a value of 0 11: iinc 2, 1 //Add 1 to slot number of local variable 2 and the value in slot is 1 14: istore_2 //Place the number in the operand stack in slot 2 of the local variable table, and the value of slot 2 becomes zero again 15: iinc 1, 1 //The value of slot 1 increases by 1 18: goto 4 //Jump to Instruction 4 21: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 24: iload_2 25: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 28: return
Since self-increment occurs on the local variable table, when iload_2, x = 0 reads into the operand stack, and iinc 21 adds itself to the local variable table, i.e., x = 1. But then istore_happened 2, x = 0 in the operand stack is stored in the local variable table, so the final result is x = 0;
Construction method
<cinit>()V
public class Demo3 { static int i = 10; static { i = 20; } static { i = 30; } public static void main(String[] args) { System.out.println(i); //The result is 30 } }
The compiler collects all static code blocks and code assigned to static members in top-down order and merges them into a special method cinit()V:
stack=1, locals=0, args_size=0 0: bipush 10 2: putstatic #3 // Field i:I 5: bipush 20 7: putstatic #3 // Field i:I 10: bipush 30 12: putstatic #3 // Field i:I 15: return
<init>()V
class Leet { public Leet(String a,int b) { this.a = a; this.b = b; } private String a = "s1"; { b = 20; } private int b = 10; { a = "s2"; } public static void main(String[] args) { Leet leet = new Leet("s3",30); System.out.println(leet.a); //"s3" System.out.println(leet.b); //30 } }
The compiler collects code for all {} code blocks and member variable assignments in top-down order to form a new construction method, but the code within the original construction method is always behind
Method Call
class Leet { public Leet() {} private void test1() {} private final void test2() {} public void test3() {} public static void test4() {} public static void main(String[] args) { Leet l = new Leet(); l.test1(); l.test2(); l.test3(); l.test4(); Leet.test4(); } }
When different methods are called, the corresponding virtual machine instructions differ
- Private, constructed, final-decorated methods are invoked with the invokespecial directive
- Ordinary member methods use the invokespecial directive when invoked. Because the content of this method cannot be determined during compilation, it can only be determined during run time
- Static methods use invokestatic instructions when invoked
Supplement:
- In JDK11, the invokevirtual directive is used except for the construction method.
Code: stack=2, locals=2, args_size=1 0: new #2 // class Leetcode/Leet 3: dup 4: invokespecial #3 // Method "<init>":()V 7: astore_1 8: aload_1 9: invokevirtual #4 // Method test1:()V 12: aload_1 13: invokevirtual #5 // Method test2:()V 16: aload_1 17: invokevirtual #6 // Method test3:()V 20: aload_1 21: pop 22: invokestatic #7 // Method test4:()V 25: invokestatic #7 // Method test4:()V 28: return
- new is the creation of the Object, allocating heap memory to the Object, and successful execution pushes the Object Reference onto the Operand Stack
- dup is at the top of the stack of assignment operands. This example is Object Reference. Why do you need two references? One is to call the object's construction method "init" with invokespecial ty 😦) V (consumes one reference at the top of the stack) and the other is assigned to a local variable with astore_1
- End method (enabling), private method (private), and construct method are all called by invokespecial directive and are static binding
- Normal member methods are invokevirtual calls, which are dynamic binding, that is, support for multiple member methods differs from static method calls in whether [object reference] is required before the method is executed
Principles of polymorphism
Since normal member methods need to be run to determine what is specific, the virtual machine needs to call the invokevirtual directive
The following steps have been taken when executing the invokevirtual directive
- Find the object first by referencing it in the stack frame
- Analyze the object header to find the actual Class of the object
- There is a vtable in the Class structure
- Query vtable to find the specific address of the method
- Byte code of execution method
abnormal
try-catch
class Leet { public static void main(String[] args) { int i = 0; try{ i = 10; }catch (Exception e) { i = 20; } } }
Byte Code
Code: stack=1, locals=3, args_size=1 0: iconst_0 1: istore_1 2: bipush 10 4: istore_1 5: goto 12 8: astore_2 9: bipush 20 11: istore_1 12: return Exception table: from to target type 2 5 8 Class java/lang/Exception
- You can see the structure of one more Exception table, [from, to] is the detection range of open before and close (that is, to detect 2-4 rows). Once the byte code execution in this range is abnormal, the exception type is matched by type, and if it is consistent, the line number indicated by target is entered
- 8-line bytecode instruction astore_2 is the location 2 where the exception object reference is stored in the local variable table (e)
Multiple single-catch
class Leet { public static void main(String[] args) { int i = 0; try { i = 10; }catch (ArithmeticException e) { i = 20; }catch (Exception e) { i = 30; } } }
Corresponding byte code
Code: stack=1, locals=3, args_size=1 0: iconst_0 1: istore_1 2: bipush 10 4: istore_1 5: goto 19 8: astore_2 // Store exception information e 9: bipush 20 11: istore_1 12: goto 19 15: astore_2 16: bipush 30 18: istore_1 19: return Exception table: from to target type 2 5 8 Class java/lang/ArithmeticException 2 5 15 Class java/lang/Exception
Because only one branch of the Exception table can be entered when an exception occurs, the slot 2 location of the local variable table is shared
finally
class Leet { public static void main(String[] args) { int i = 0; try { i = 10; } catch (Exception e) { i = 20; } finally { i = 30; } } }
Corresponding byte code
Code: stack=1, locals=4, args_size=1 0: iconst_0 1: istore_1 2: bipush 10 4: istore_1 5: bipush 30 //finally 7: istore_1 8: goto 27 11: astore_2 // Store exception information e 12: bipush 20 14: istore_1 15: bipush 30 //finally 17: istore_1 18: goto 27 21: astore_3 22: bipush 30 //finally 24: istore_1 25: aload_3 26: athrow 27: return Exception table: from to target type 2 5 11 Class java/lang/Exception 2 5 21 any 11 15 21 any
You can see that three copies of the code in En nally are copied into the try process, the catch process, and the rest of the catch's exception type process
Note: Although it appears from the byte code directive that there are finally blocks in each block, the code in the finally block will only be executed once
return in finally
class Leet { public static void main(String[] args) { int i = Leet.test(); System.out.println(i); } public static int test() { int i; try { i = 10; return i; } finally { i = 20; return i; } } }
Corresponding byte code
Code: stack=1, locals=3, args_size=0 0: bipush 10 2: istore_0 3: iload_0 4: istore_1 //Temporary return value 5: bipush 20 7: istore_0 8: iload_0 9: ireturn //ireturn returns an integer value of 20 at the top of the operand stack //If an exception occurs, the contents of the finally block are executed without throwing an exception 10: astore_2 11: bipush 20 13: istore_0 14: iload_0 15: ireturn //There is no athrow here, that is, if there is a return operation in the finally block and an exception occurs in the try block, the exception will be swallowed! Exception table: from to target type 0 5 10 any
- Since ireturn in nally is inserted into all possible processes, the return must be based on nally
- Compared with Ennally in the example above, we found no athrow s, which tells us that if there is a return in Ennally, the exception will be swallowed
- Do not return in finally
Swallowed anomaly
class Leet { public static void main(String[] args) { int i = Leet.test(); //The final result is 20 System.out.println(i); } public static int test() { int i; try { i = 10; //There should be an exception thrown here i = i/0; return i; } finally { i = 20; return i; } } }
You will find that the print result is 20 and no exceptions were thrown
finally without return
class Leet { public static void main(String[] args) { int i = Leet.test(); System.out.println(i); } public static int test() { int i = 10; try { return i; } finally { i = 20; } } }
Corresponding byte code
Code: stack=1, locals=3, args_size=0 0: bipush 10 2: istore_0 //Assign to i 10 3: iload_0 //Load to top of operand stack 4: istore_1 //Location 1 loaded into the local variable table 5: bipush 20 7: istore_0 //Assign to i 20 8: iload_1 //Load number 10 from position 1 of local variable table to operand stack 9: ireturn //Returns the top element 10 of the operand stack 10: astore_2 11: bipush 20 13: istore_0 14: aload_2 //Load Exception 15: athrow //throw Exception table: from to target type 3 5 10 any
2. Compiler Processing
To run the source code, it must first be converted to binary machine code. This is the task of the compiler. You can know how grammatical sugar works by decompiling byte codes. So let's also put it in the class file.
Grammatical Sugar
Java provides many syntax sugars, simple programmer's syntax.
Default constructor
public class Candy1 { }
After compile-time optimization
public class Candy1 { //This parameterless constructor was added for us by the java compiler public Candy1() { //This calls the parameterless constructor of the parent class Object, which calls java/lang/Object. "<init>": ()V super(); } }
Automatically unpacking boxes
The process of converting a basic type to its packaging type is called Unboxing
After JDK 5, their conversion can be automated at compile time
public class Demo2 { public static void main(String[] args) { Integer x = 1; int y = x; } }
The conversion process is as follows
public class Demo2 { public static void main(String[] args) { //The basic type is assigned to the packaging type, called packing Integer x = Integer.valueOf(1); //The packaging type is assigned to the basic type, called Unboxing int y = x.intValue(); } }
Generic Set Value
Generics are also a feature that was added in JDK 5, but after compiling generic code, java performs a generic erase action, that is, generic information is lost after compiling into byte code, and the actual types are treated as Object types:
public class Demo3 { public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(10); Integer x = list.get(0); } }
Corresponding byte code
Code: stack=2, locals=3, args_size=1 0: new #2 // class java/util/ArrayList 3: dup 4: invokespecial #3 // Method java/util/ArrayList."<init>":()V 7: astore_1 8: aload_1 9: bipush 10 11: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; //A generic erase is done here, and the actual call is add(Objcet o) 14: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z 19: pop 20: aload_1 21: iconst_0 //Generic erase is also done here, and get(Object o) is actually called 22: invokeinterface #6, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object; //Type conversion, converting Object to Integer 27: checkcast #7 // class java/lang/Integer 30: astore_2 31: return
So when the get function is called, there is a type conversion operation
Integer x = (Integer) list.get(0);
If you assign the return result to a variable of type int, there is also an automatic unboxing operation
int x = (Integer) list.get(0).intValue();
Variable parameters
public class Demo4 { public static void foo(String... args) { //Assigning args to arr shows that String... It's actually String[] String[] arr = args; System.out.println(arr.length); } public static void main(String[] args) { foo("hello", "world"); } }
The variable parameter String...args is actually a String[] args, as you can see from the assignment statements in the code. Similarly, the java compiler transforms the above code during compilation to:
public class Demo4 { public Demo4 {} public static void foo(String[] args) { String[] arr = args; System.out.println(arr.length); } public static void main(String[] args) { foo(new String[]{"hello", "world"}); } }
Note that if foo() is called, the equivalent code is foo(new String[]{}) when the parameter is not passed, creating an empty array instead of a null passed directly
foreach
public class Demo5 { public static void main(String[] args) { //Simplified writing of array assignments is also a grammatical sugar. int[] arr = {1, 2, 3, 4, 5}; for(int x : arr) { System.out.println(x); } } }
The compiler will help us convert to
public class Demo5 { public Demo5 {} public static void main(String[] args) { int[] arr = new int[]{1, 2, 3, 4, 5}; for(int i=0; i<arr.length; ++i) { int x = arr[i]; System.out.println(x); } } }
Use foreach for collections
public class Demo5 { public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); for (Integer x : list) { System.out.println(x); } } }
To use foreach for a collection, the collection class is required to implement the Iterable interface, since iterator Iterator is required to traverse the collection
public class Demo5 { public Demo5 {} public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); //The iterator that gets the set Iterator<Integer> iterator = list.iterator(); while(iterator.hasNext()) { Integer x = iterator.next(); System.out.println(x); } } }
Switch string/switch enumeration
public class Demo6 { public static void main(String[] args) { String str = "hello"; switch (str) { case "hello" : System.out.println("h"); break; case "world" : System.out.println("w"); break; default: break; } } }
Actions performed in the compiler
public class Demo6 { public Demo6() { } public static void main(String[] args) { String str = "hello"; int x = -1; //Using hashCode+value of a string to determine whether a match occurs switch (str.hashCode()) { //hello's hashCode case 99162322 : //Compare again because the hashCode s of strings may be equal if(str.equals("hello")) { x = 0; } break; //hashCode of world case 11331880 : if(str.equals("world")) { x = 1; } break; default: break; } //Using the second switch for output judgment switch (x) { case 0: System.out.println("h"); break; case 1: System.out.println("w"); break; default: break; } } }
Process description:
- During compilation, a single switch is divided into two
- The first one matches the string and assigns x a value
- String matching uses hashCode for strings and equals method
- HashCode is used to improve efficiency, and equals is used to prevent hashCode conflicts (such as BM and C.)
- The second is used to determine the output statement based on the value of x
- The first one matches the string and assigns x a value
enumeration
public class Demo7 { public static void main(String[] args) { SEX sex = SEX.MALE; switch (sex) { case MALE: System.out.println("man"); break; case FEMALE: System.out.println("woman"); break; default: break; } } } enum SEX { MALE, FEMALE; }
The code executed in the compiler is as follows
public class Demo7 { /** * Define a composite class (used only by jvm, not visible to us) * Relationship between ordinal and array elements used to map enumerations * The ordinal of the enumeration represents the ordinal of the enumerated object, starting at 0 * That is, MALE ordinal()=0, FEMALE ordinal()=1 */ static class $MAP { //The size of the array is the number of enumerated elements that hold the numbers used by case for comparison static int[] map = new int[2]; static { //ordinal is where the enumeration element corresponds, MALE 0, FEMALE 1 map[SEX.MALE.ordinal()] = 1; map[SEX.FEMALE.ordinal()] = 2; } } public static void main(String[] args) { SEX sex = SEX.MALE; //Assign the value of the corresponding location enumeration element to x for the case operation int x = $MAP.map[sex.ordinal()]; switch (x) { case 1: System.out.println("man"); break; case 2: System.out.println("woman"); break; default: break; } } } enum SEX { MALE, FEMALE; }
Enum Class
enum SEX { MALE, FEMALE; }
Converted code
public final class Sex extends Enum<Sex> { //Elements in corresponding enumeration classes public static final Sex MALE; public static final Sex FEMALE; private static final Sex[] $VALUES; static { //Call the constructor, pass in the value of the enumeration element and ordinal MALE = new Sex("MALE", 0); FEMALE = new Sex("FEMALE", 1); $VALUES = new Sex[]{MALE, FEMALE}; } //Call method in parent class private Sex(String name, int ordinal) { super(name, ordinal); } public static Sex[] values() { return $VALUES.clone(); } public static Sex valueOf(String name) { return Enum.valueOf(Sex.class, name); } }
Anonymous Inner Class
public class Demo8 { public static void main(String[] args) { Runnable runnable = new Runnable() { @Override public void run() { System.out.println("running..."); } }; } }
Converted code
public class Demo8 { public static void main(String[] args) { //Create anonymous internal class objects with extra created classes Runnable runnable = new Demo8$1(); } } //An extra class was created that implements the Runnable interface final class Demo8$1 implements Runnable { public Demo8$1() {} @Override public void run() { System.out.println("running..."); } }
If a local variable is referenced in an anonymous inner class
public class Demo8 { public static void main(String[] args) { int x = 1; Runnable runnable = new Runnable() { @Override public void run() { System.out.println(x); } }; } }
Translate code
public class Demo8 { public static void main(String[] args) { int x = 1; Runnable runnable = new Runnable() { @Override public void run() { System.out.println(x); } }; } } final class Demo8$1 implements Runnable { //One more variable was created int val$x; //Become a parametric constructor public Demo8$1(int x) { this.val$x = x; } @Override public void run() { System.out.println(val$x); } }