【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." ":()V
      super();
   }
}
  1. 原始代码在经过编译优化后会添加一个无参构造,这就是默认构造方法。

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


二、自动拆装箱

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

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();
   }
}
  1. 在代码经过编译优化后,可以发现会通过添加一些固定的代码,减少程序员负担。

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


二、泛型集合取值

泛型也是在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."":()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();

你可能感兴趣的:(JVM,java,jvm)