2021-08-31 [JVM Series-3] Class File

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 machines

Class 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

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);
   }
}

Keywords: Java jvm

Added by jeff8j on Sat, 18 Dec 2021 22:18:14 +0200