Java provides many different flavors of grammar sugar, each flavor of sugar has a different way to eat and magic, which flavors of sugar do you know? Let’s try it together

Generics and type erasure

  • Generics in Java exist only at compile time, because only at compile time are the types of generics checked. At run time, there is no concept of generics. After javac is compiled, generics are erased.

  • Example 1 — Determine bytecode objects

    public class Test {
    
        // 
        public static void main(String[] args) {
    
            ArrayList<String> list1 = new ArrayList<String>();
            list1.add("abc");
    
            ArrayList<Integer> list2 = new ArrayList<Integer>();
            list2.add(123);
    
            // true Different generics, but the class objects are the sameSystem.out.println(list1.getClass() == list2.getClass()); }}Copy the code
  • Example 2 — Reflection gets run-time objects

    public static void main(String[] args) throws Exception {
        List<String> list = new ArrayList<>();
        list.add("一");
    
        Method method = list.getClass().getMethod("add", Object.class);
    
        method.invoke(list, 2);
    
        System.out.println(list); // [一, 2]
    }
    Copy the code

    From the example above, you can see that different types can be set directly into a collection at run time. Although the collection’s generics specify other types, generics only restrict types at compile time

Automatic packing and unpacking

  • When using Integer, int and other wrapped classes and base types to judge, we can directly compare the reference type with the base type without converting the reference type. As we know, the reference type can be compared directly, then the memory address can be compared.

  • The principle of

    // Integer and int are directly compared
    public static void main(String[] args) throws Exception {
        Integer a = new Integer(1);
        int b = 1;
        System.out.println(a == b); // true
    }
    
    // Integer automatic unpacking principle
    public static void main(String[] args) throws Exception {
        Integer a = new Integer(1);
        int b = 1;
    
        / / split open a case
        int i = a.intValue();
    }
    
    // Automatic boxing of Integer
    public static void main(String[] args) throws Exception {
        Integer c = 1;
        // c is boxed as an Integer object
        Integer d = Integer.valueOf(1);
    }
    Copy the code

    In fact, the automatic boxing and unboxing is done by calling Integer’s intValue() and valueOf() methods. The JVM automatically calls methods when a wrapper class needs to be converted to a base type or when the base type needs to be converted to a wrapper class.

Traversal cycle

  • ForEach enhances for loops the most common way we iterate over sets of numbers or collections, so how does forEach support clean code implementation loops? ForEach is also a syntactic sugar

  • Example:

    • Original traversal mode:
    public static void main(String[] args) throws Exception {
        List<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");
        list.add("3");
    
        for(String s : list) { System.out.println(s); }}Copy the code
    • Compiled code:
    public static void main(String[] args) throws Exception {
        List<String> list = new ArrayList();
        list.add("1");
        list.add("2");
        list.add("3");
        
        Iterator var2 = list.iterator();
        while(var2.hasNext()) { String s = (String)var2.next(); System.out.println(s); }}Copy the code

    As can be seen from the decompiled code, forEach actually uses iterators to loop, but the tedious operations of iterators are left to the JVM. We only need to use concise forEach to complete the loop

Conditional compilation

  • The JVM evaluates the conditions of an if statement, eliminating the code for branches that won’t hold, and reducing code instructions

  • Example:

    • The source code:
    public static void main(String[] args) throws Exception {
        if(true) {
            System.out.println("true");
        } else {
            System.out.println(false); }}Copy the code
    • Decompiled code:
    public static void main(String[] args) throws Exception {
        System.out.println("true");
    }
    Copy the code

    This is how the JVM optimizes code, and conditional compilation can only occur when the condition is considered constant

Swtich Supports strings

  • Prior to jdk1.7, switch supported basic data types: byte, short, char, int, and corresponding wrapper classes. Byte, short, and char can be converted to ints. Long, double, and float must be cast to ints. However, string is not allowed, and an error will be reported, so how is string supported after jdk1.8?

  • Principle:

    Jdk1.8 does not add directives to handle strings. Instead, string#hashcode calculates the hash value for each string and matches cases in the switch

    Now you might be wondering, what if there’s a hash conflict? The JVM development team also figured out that, like the HashMap solution, only the hashCode and equals methods would guarantee equality in case of hash conflicts

  • Example:

    • The source code:
    public static void main(String[] args) {
        String str = "a";
    
        switch (str) {
            case "a":
                System.out.println("a");
                break;
            case "b":
                System.out.println("b");
                break; }}Copy the code
    • Decompiled code:
    public static void main(String[] args) {
        String str = "a";
        byte var3 = -1;
        // Determine whether hashcode is equal to equals, and if so, assign int
        switch(str.hashCode()) {
            case 97:  // The hashcode of 'a' calculates to be 97
                if (str.equals("a")) {
                    var3 = 0;
                }
                break;
            case 98:
                if (str.equals("b")) {
                    var3 = 1; }}// Use the switch of int
        switch(var3) {
            case 0:
                System.out.println("a");
                break;
            case 1:
                System.out.println("b"); }}Copy the code

try-with-resource

  • Let’s take a look at the code for a try-catch-finally operation on a stream

  • Example: The operation of reading a file and closing the stream correctly

    public static void main(String[] args) {
        InputStream in = null;
        try {
            in = new FileInputStream("D://file.txt");
            int read = in.read();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(in ! =null) {
                try {
                    in.close();
                } catch(IOException e) { e.printStackTrace(); }}}}Copy the code

    Ah, there is only one line of code to read the file, but there is so much code to secure the stream

  • The try – with – the resource example

    public static void main(String[] args) {
        try(InputStream in = new FileInputStream("D://file.txt")) {
            int read = in.read();
        } catch(Exception e) { e.printStackTrace(); }}Copy the code

    Putting the read code operations in the try block automatically helps us safely close the stream. How? This is basically the same as the original try-catch-finally, except that the JVM does all the work

    • Decompiled code
    public static void main(String[] args) {
        try {
            InputStream in = new FileInputStream("D://file.txt");
            Throwable var2 = null;
    
            try {
                int var3 = in.read();
            } catch (Throwable var12) {
                var2 = var12;
                throw var12;
            } finally {
                if(in ! =null) {
                    if(var2 ! =null) {
                        try {
                            in.close();
                        } catch(Throwable var11) { var2.addSuppressed(var11); }}else{ in.close(); }}}}catch(Exception var14) { var14.printStackTrace(); }}Copy the code

    This is basically the same as the self-handling exception above, but the JVM handles it more fully and provides a sweet icing to use

Variable-length argument

  • When you’re passing parameters to a method, and you’re not sure how many you’re passing, you can use variable-length parameters as the parameters of the method, but variable-length parameters are essentially arrays, so you can take multiple parameters, and when you’re processing them in a method, you need to treat them as arrays

  • The sample

    • Types of variable length arguments:
    public static void main(String[] args) {
        int a = 1;
        int b = 2;
        int c = 3;
        handle(a, b, c);
    }
    
    
    private static void handle(int a, int. arr) {
        System.out.println(arr.getClass().getSimpleName()); // int[]
    }
    Copy the code

    The variable length parameter is of type int[], which indicates that multiple parameters are passed but stored in the array. Note that variable length parameters can only be placed at the end of the parameter list, and only one variable length parameter can be found in a method

The enumeration

  • Do you know that enumerations are essentially classes? Enumerations are essentially implemented by ordinary classes, but the compiler handles them for us. Each enumerated type inherits from java.lang.Enum and automatically adds values and valueOf methods

    Let’s take a look at what enumeration looks like after decompilation

  • The source code:

    public enum Skip {
        ADD,
        SUBTRACT,
        MULTIPLY,
        DIVIDE
    }
    Copy the code
  • Decompiled code:

    • The first stepcompile:javac Skip.java
    • The second stepdecompiling:javap -c -v Skip.class
    Compiled from "Skip.java"
    public final class Skip extends java.lang.Enum<Skip>
      minor version: 0
      major version: 52
      flags: ACC_PUBLIC.ACC_FINAL.ACC_SUPER.ACC_ENUM
    Constant pool# 1:= Fieldref           #4.#38         // Skip.$VALUES:[LSkip;
       #2 = Methodref          #39.#40        // "[LSkip;".clone:()Ljava/lang/Object;
       #3 = Class              #23            // "[LSkip;"
       #4 = Class              #41            // Skip
       #5 = Methodref          #16.#42        // java/lang/Enum.valueOf:(Ljava/lang/Class; Ljava/lang/String;) Ljava/lang/Enum;
       #6 = Methodref          #16.#43        // java/lang/Enum."
            
             ":(Ljava/lang/String; I)V
            
       #7 = String             #17            // ADD
       #8 = Methodref          #4.#43         // Skip."
            
             ":(Ljava/lang/String; I)V
            
       #9 = Fieldref           #4.#44         // Skip.ADD:LSkip;
      #10 = String             #19            // SUBTRACT
      #11 = Fieldref           #4.#45         // Skip.SUBTRACT:LSkip;
      #12 = String             #20            // MULTIPLY
      #13 = Fieldref           #4.#46         // Skip.MULTIPLY:LSkip;
      #14 = String             #21            // DIVIDE
      #15 = Fieldref           #4.#47         // Skip.DIVIDE:LSkip;
      #16 = Class              #48            // java/lang/Enum
      #17 = Utf8               ADD
      #18 = Utf8               LSkip;
      #19 = Utf8               SUBTRACT
      #20 = Utf8               MULTIPLY
      #21 = Utf8               DIVIDE
      #22 = Utf8               $VALUES
      #23 = Utf8               [LSkip;
      #24 = Utf8               values
      #25 = Utf8               ()[LSkip;
      #26 = Utf8               Code
      #27 = Utf8               LineNumberTable
      #28 = Utf8               valueOf
      #29= Utf8 (Ljava/lang/String;) LSkip; #30 = Utf8               <init>
      #31= Utf8 (Ljava/lang/String; I)V #32 = Utf8               Signature
      #33 = Utf8               ()V
      #34 = Utf8               <clinit>
      #35= Utf8 Ljava/lang/Enum<LSkip; >; #36 = Utf8               SourceFile
      #37 = Utf8               Skip.java
      #38 = NameAndType        #22: #23        // $VALUES:[LSkip;
      #39 = Class              #23            // "[LSkip;"
      #40 = NameAndType        #49: #50        // clone:()Ljava/lang/Object;
      #41 = Utf8               Skip
      #42 = NameAndType        #28: #51        // valueOf:(Ljava/lang/Class; Ljava/lang/String;) Ljava/lang/Enum;
      #43 = NameAndType        #30: #31        // "
            
             ":(Ljava/lang/String; I)V
            
      #44 = NameAndType        #17: #18        // ADD:LSkip;
      #45 = NameAndType        #19: #18        // SUBTRACT:LSkip;
      #46 = NameAndType        #20: #18        // MULTIPLY:LSkip;
      #47 = NameAndType        #21: #18        // DIVIDE:LSkip;
      #48 = Utf8               java/lang/Enum
      #49 = Utf8               clone
      #50 = Utf8               ()Ljava/lang/Object;
      #51= Utf8 (Ljava/lang/Class; Ljava/lang/String;) Ljava/lang/Enum; {public static final Skip ADD;
        descriptor: LSkip;
        flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
    
      public static final Skip SUBTRACT;
        descriptor: LSkip;
        flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
    
      public static final Skip MULTIPLY;
        descriptor: LSkip;
        flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
    
      public static final Skip DIVIDE;
        descriptor: LSkip;
        flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
    
      public static Skip[] values();
        descriptor: ()[LSkip;
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=1, locals=0, args_size=0
             0: getstatic     #1                  // Field $VALUES:[LSkip;
             3: invokevirtual #2                  // Method "[LSkip;".clone:()Ljava/lang/Object;
             6: checkcast     #3                  // class "[LSkip;"
             9: areturn
          LineNumberTable:
            line 5: 0
    
      public static Skip valueOf(java.lang.String); descriptor: (Ljava/lang/String;) LSkip; flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1
             0: ldc           #4                  // class Skip
             2: aload_0
             3: invokestatic  #5                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class; Ljava/lang/String;) Ljava/lang/Enum;
             6: checkcast     #4                  // class Skip
             9: areturn
          LineNumberTable:
            line 5: 0
    
      static {};
        descriptor: ()V
        flags: ACC_STATIC
        Code:
          stack=4, locals=0, args_size=0
             0: new           #4                  // class Skip
             3: dup
             4: ldc           #7                  // String ADD
             6: iconst_0
             7: invokespecial #8                  // Method "
            
             ":(Ljava/lang/String; I)V
            
            10: putstatic     #9                  // Field ADD:LSkip;
            13: new           #4                  // class Skip
            16: dup
            17: ldc           #10                 // String SUBTRACT
            19: iconst_1
            20: invokespecial #8                  // Method "
            
             ":(Ljava/lang/String; I)V
            
            23: putstatic     #11                 // Field SUBTRACT:LSkip;
            26: new           #4                  // class Skip
            29: dup
            30: ldc           #12                 // String MULTIPLY
            32: iconst_2
            33: invokespecial #8                  // Method "
            
             ":(Ljava/lang/String; I)V
            
            36: putstatic     #13                 // Field MULTIPLY:LSkip;
            39: new           #4                  // class Skip
            42: dup
            43: ldc           #14                 // String DIVIDE
            45: iconst_3
            46: invokespecial #8                  // Method "
            
             ":(Ljava/lang/String; I)V
            
            49: putstatic     #15                 // Field DIVIDE:LSkip;
            52: iconst_4
            53: anewarray     #4                  // class Skip
            56: dup
            57: iconst_0
            58: getstatic     #9                  // Field ADD:LSkip;
            61: aastore
            62: dup
            63: iconst_1
            64: getstatic     #11                 // Field SUBTRACT:LSkip;
            67: aastore
            68: dup
            69: iconst_2
            70: getstatic     #13                 // Field MULTIPLY:LSkip;
            73: aastore
            74: dup
            75: iconst_3
            76: getstatic     #15                 // Field DIVIDE:LSkip;
            79: aastore
            80: putstatic     #1                  // Field $VALUES:[LSkip;
            83: return
          LineNumberTable:
            line 6: 0
            line 7: 13
            line 8: 26
            line 9: 39
            line 5: 52
    }
    Signature: #35                          // Ljava/lang/Enum
            
             ;
            ;>
    SourceFile: "Skip.java"
    Copy the code

    As you can see from the decomcompiled code, the enumeration Class defined by Skip extends java.lang.Enum is a Class object and inherits from the java.lang.Enum Class, automatically generating values and valueOf methods.

    Each enumerated constant is a static constant field, implemented using an inner class that inherits from the enumerated class. All enumerated constants are initialized by static code blocks, that is, during class loading. In addition, the clone, readObject and writeObject methods are defined as final, and corresponding exceptions are thrown. This ensures that each enumeration type and enumeration constant is immutable. These two features of enumeration can be leveraged to implement thread-safe singletons.

With all that said about syntactic sugar, do you know at what stage in javac compilation the syntactic sugar is unsugar-coated? That’s right, in the analysis and bytecode generation phase, where the Javac compiler specifically unsugarcoats the syntax, returning it to the original code

Next time you’re eating grammar candy, don’t forget the source code below each sugar coating

Welcome to the wechat public account “code on the finger tip”

Original is not easy, click a praise and then go ~ welcome to pay attention to, to bring you more wonderful articles!

Your likes and attention are the biggest motivation for writing articles