• 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 }
  • 相关阅读:
    [Kafka]
    [Kafka]
    [数据挖掘]
    接口部署说明
    报表部署安装说明
    kafka单机安装测试-原创-本机测试过
    centos安装nginx 带upstream
    spring boot jpa mysql porm文件备份 可以运行的
    Spring boot jpa mysql 连接数据库SSL错误
    mysql 学习笔记
  • 原文地址:https://www.cnblogs.com/wxdlut/p/14200676.html
Copyright © 2020-2023  润新知