• 【JVM类加载及字节码技术】编译期处理语法糖构造、泛型、拆装箱(一)


    前言

    一、默认构造函数

    • 1.案例代码
    • 2.编译优化后

    二、自动拆装箱

    • 1.案例代码
    • 2.编译优化后

    二、泛型集合取值

    • 1.案例代码
    • 2.字节码文件

    前言

    语法糖:其实就是指java编译器把 .java 源文件编译为 .class 字节码的过程,自动生成和转换的代码,主要是为了减轻程序员的负担,算是java编译器给我们的额外福利。

    注意,以下代码的分析,借助了javap工具,idea的反编译功能,idea插件jclasslib等工具,编译器转换的结果就是class字节码,只是为了便于阅读,给出几乎等价的Java源码方式,并不是编译器还会转换出中间的Java源码。

    提示:以下是本篇文章正文内容,下面案例可供参考

    一、默认构造函数
    1.案例代码
    代码如下:

    public class Candy1 {
    
    }
    

    2.编译优化后
    代码如下:

    public class Candy1 {
    //这个无参构造器是java编译器帮我们加上的
    public Candy1() {
    //即调用父类 Object 的无参构造方法,即调用 java/lang/Object." <init>":()V
    super();
    }
    }
    

    原始代码在经过编译优化后会添加一个无参构造,这就是默认构造方法。

    在默认构造方法中会调用父类的无参构造。

    二、自动拆装箱
    基本类型和其他包装类型的相互转换过程,称为拆装箱。

    在JDK5以后,它们的转换都可以在编译器自动完成。

    1.案例代码
    代码如下:

    public class Demo2 {
    public static void main(String[] args) {
    Integer x = 1;
    int y = x;
    }
    }
    

    2.编译优化后
    代码如下:

    public class Demo2 {
    public static void main(String[] args) {
    //基本类型赋值给包装类型,称为装箱
    Integer x = Integer.valueOf(1);
    //包装类型赋值给基本类型,称谓拆箱
    int y = x.intValue();
    }
    }
    

    在代码经过编译优化后,可以发现会通过添加一些固定的代码,减少程序员负担。

    在拆箱和装箱过程中,因为代码都是固定不变,所以可以进行优化。

    二、泛型集合取值
    泛型也是在JDK 5 开始加入的特性,但Java在编译泛型代码后会执行泛型擦除,即泛型信息在编译为字节码之后就丢失了,实际的类型都当作了Object类型来处理。

    1.案例代码
    代码如下:

    public class Demo3 {
    public static void main(String[] args) {
    List<Integer> list = new ArrayList<>();
    list.add(10);
    Integer x = list.get(0);
    }
    }
    

    2.字节码文件
    字节码如下:

    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;
    
    
    //这里进行了泛型擦除,实际调用的是add(Objcet o)
    14: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
    
    19: pop
    20: aload_1
    21: iconst_0
    //这里也进行了泛型擦除,实际调用的是get(Object o)
    22: invokeinterface #6, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
    
    
    //这里进行了类型转换,将Object转换成了Integer
    27: checkcast #7 // class java/lang/Integer
    
    30: astore_2
    31: return
    

    在上图字节码文件中,我们可以重点关注14以及27行指令,分别对应擦除,以及转换。

    14行指令这里进行了泛型擦除,实际调用的是add(Objcet o)。

    //这里进行了泛型擦除,实际调用的是add(Objcet o)
    14: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z

    27行指令这里进行了类型转换,将Object转换成了Integer
    //这里进行了类型转换,将Object转换成了Integer
    27: checkcast #7 // class java/lang/Integer

    在调用get函数取值时,有一个类型转换的操作:

    Integer x = (Integer) list.get(0);

    如果要将返回结果赋值给一个int类型的变量,则还有自动拆箱的操作:

    int x = (Integer) list.get(0).intValue();

  • 相关阅读:
    嵌入式Linux设备驱动编程(1):基础
    嵌入式Linux网络编程
    Linux进程间通信(5):消息队列
    Android网络通信(2):HTTP通信
    Android网络通信(3):Socket通信
    Android网络通信(5):WiFi
    Linux任务、进程和线程
    Android程序的安装和卸载
    Android网络通信(4):WebKit
    Android网络通信(1):Android网络基础
  • 原文地址:https://www.cnblogs.com/cfas/p/15960339.html
Copyright © 2020-2023  润新知