JVM-Java语法糖与Java编译器

  基本类型和其包装类型之间的自动转换,也就是自动装箱、自动拆箱,是通过加入[Wrapper].valueOf(如 Integer.valueOf)以及[Wrapper].[primitive]Value(如 Integer.intValue)方法调用来实现的。

  Java 程序中的泛型信息会被擦除。具体来说,Java 编译器将选取该泛型所能指代的所有类中层次最高的那个,作为替换泛型的具体类。

  由于 Java 语义与 Java 字节码中关于重写的定义并不一致,因此 Java 编译器会生成桥接方法作为适配器。

  此外,我还介绍了 foreach 循环以及字符串 switch 的编译。

  1. 自动装箱、自动拆箱
  2. 泛型擦除
  3. foreach循环的编译
  4. switch的编译

1. 自动装箱、自动拆箱 

1 public int foo() {
2   ArrayList<Integer> list = new ArrayList<>();
3   list.add(0);  // Integer.valueOf()自动装箱  (基本型--> 包装类型)
4   int result = list.get(0);    // Integer.intValue()的自动拆箱
5   return result;
6 }

 

public int foo();
  Code:
     0: new java/util/ArrayList
     3: dup
     4: invokespecial java/util/ArrayList."<init>":()V
     7: astore_1
     8: aload_1
     9: iconst_0
    10: invokestatic java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    13: invokevirtual java/util/ArrayList.add:(Ljava/lang/Object;)Z
    16: pop
    17: aload_1
    18: iconst_0
    19: invokevirtual java/util/ArrayList.get:(I)Ljava/lang/Object;
    22: checkcast java/lang/Integer
    25: invokevirtual java/lang/Integer.intValue:()I
    28: istore_2
    29: iload_2
    30: ireturn

 

1 public static Integer valueOf(int i) {
2     if (i >= IntegerCache.low && i <= IntegerCache.high)
3         return IntegerCache.cache[i + (-IntegerCache.low)];  // 直接从缓存返回,否则new一个对象
4     return new Integer(i);
5 }

 

2. 泛型擦除

 1 package java8;
 2 
 3 import java.util.ArrayList;
 4 
 5 /**
 6  * 既然泛型会被类型擦除,那么我们还有必要用它吗?我认为是有必要的。Java 编译器可以根据泛型参数判断程序中的语法是否正确。
 7  * 举例来说,尽管经过类型擦除后,ArrayList.add 方法所接收的参数是 Object 类型,但是往泛型参数为 Integer 类型的
 8  *          ArrayList 中添加字符串对象,Java 编译器是会报错的。
 9  */
10 public class Foo {
11 
12     public int foo() {
13 
14         /**
15          * 字节码如下
16          13: invokevirtual java/util/ArrayList.add:(Ljava/lang/Object;)Z
17          ...
18          19: invokevirtual java/util/ArrayList.get:(I)Ljava/lang/Object;
19          22: checkcast java/lang/Integer
20 
21          生成的字节码中,往 ArrayList 中添加元素的 add 方法,所接受的参数类型是 Object22          而从 ArrayList 中获取元素的 get 方法,其返回类型同样也是 Object23          */
24         ArrayList<Integer> list = new ArrayList<>();
25         list.add(0);
26         int result = list.get(0);
27         return result;
28     }
29 }
30 
31 
32 class GenericTest<T extends Number> {
33 
34     /**
35      * 当然,并不是每一个泛型参数被擦除类型后都会变成 Object 类。对于限定了继承类的泛型参数,经过类型擦除后,
36      * 所有的泛型参数都将变成所限定的继承类。
37      * 可以看到,foo 方法的方法描述符所接收参数的类型以及返回类型都为 Number。
38      *
39      *
40      T foo(T);
41      descriptor: (Ljava/lang/Number;)Ljava/lang/Number;
42      flags: (0x0000)
43      Code:
44      stack=1, locals=2, args_size=2
45      0: aload_1
46      1: areturn
47      Signature: (TT;)TT;
48      *
49      */
50     T foo(T t) {
51         return t;
52     }
53 }

 

3. foreach循环的编译

 foreach 循环允许 Java 程序在 for 循环里遍历数组或者 Iterable 对象。对于数组来说,foreach 循环将从 0 开始逐一访问数组中的元素,直至数组的末尾。其等价的代码如下面所示:

 1 public void foo(int[] array) {
 2   for (int item : array) {
 3   }
 4 }
 5 // 等同于
 6 public void bar(int[] array) {
 7   int[] myArray = array;
 8   int length = myArray.length;
 9   for (int i = 0; i < length; i++) {
10     int item = myArray[i];
11   }
12 }

 

对于 Iterable 对象来说,foreach 循环将调用其 iterator 方法,并且用它的 hasNext 以及 next 方法来遍历该 Iterable 对象中的元素。其等价的代码如下面所示:

 1 public void foo(ArrayList<Integer> list) {
 2   for (Integer item : list) {
 3   }
 4 }
 5 // 等同于
 6 public void bar(ArrayList<Integer> list) {
 7   Iterator<Integer> iterator = list.iterator();
 8   while (iterator.hasNext()) {
 9     Integer item = iterator.next();
10   }
11 }

4. switch的编译

 1 package java8;
 2 
 3 /**
 4  * 字符串 switch 编译而成的字节码看起来非常复杂,但实际上就是一个哈希桶。由于每个 case 所截获的字符串都是常量值,
 5  * 因此,Java 编译器会将原来的字符串 switch 转换为 int 值 switch,比较所输入的字符串的哈希值。
 6  *
 7  * 由于字符串哈希值很容易发生碰撞,因此,我们还需要用 String.equals 逐个比较相同哈希值的字符串。
 8  *
 9  * tableswitch用于case比较紧凑的代码,而lookup用于case比较分散的代码。
10  * 如果不考虑空间的话,tableswitch指令比lookup指令有更高的执行效率。
11  */
12 public class SwitchTest {
13 
14     /**
15      *  1: lookupswitch  { // 2
16      *                        1: 28
17      *                        2: 39
18      *                  default: 50
19      *             }
20      *
21      */
22     void switchGo(int inPut) {
23         switch (inPut) {
24             case 1:
25                 System.out.println("***********");
26                 break;
27             case 2:
28                 System.out.println("#########");
29                 break;
30             default:
31                 System.out.println("DEFAULT_&&");
32                 break;
33         }
34     }
35 
36 
37     /**
38      *  1: tableswitch   { // 0 to 2
39      *                        0: 28
40      *                        1: 30
41      *                        2: 32
42      *                  default: 34
43      *             }
44      *
45      *
46      */
47     int chooseNear(int i) {
48         switch (i) {
49             case 0:  return  0;
50             case 1:  return  1;
51             case 2:  return  2;
52             default: return -1;
53         }
54     }
55 }

 

posted on 2020-12-28 11:21  gogoy  阅读(91)  评论(0编辑  收藏  举报

导航