通过字节码分析Java中自动装箱和拆箱是如何实现的

↘锁芯ラ 提交于 2020-08-12 16:37:59

云栖号资讯:【点击查看更多行业资讯
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!


Java中自动装箱和拆箱

装箱(Boxing),也称为包装(Wrapper),是在对象中放置原语类型(primitive type)的过程,以便原语(primitive)可以作为引用对象使用。

这里的primitive type就是Java里面的基本类型,所有的基本类型都有一个与之对应的类。例如,Integer类对应基本类型int。

通常,这些类称为包装器(wrapper)。这些对象包装器类拥有很明显的名字:Integer、Long、Float、Double、Short、Byte、Character、Void和Boolean(前6个类派生于公共的超类Number)。

对象包装器类是不可变的,即一旦构造了包装器,就不允许更改包装在其中的值。同时,对象包装器类还是final,因此不能定义它们的子类。

自动装箱是指通过类型转换(隐式或显式)从值类型中获取引用类型,这部分工作是编译器帮我们来完成的。

我们看一个常见的例子,比如我们创建一个int类型的ArrayList(因为ArrayList的泛型是不允许基本类型的,这里只能使用它们包装类),我们给ArrayList添加元素,再从里面获取元素,一般是这么写的:

// int类型的自动装箱和拆箱
ArrayList<Integer> integerArrayList = new ArrayList<>();
integerArrayList.add(1);
int i = integerArrayList.get(0);

这里分别触发了自动装箱和自动拆箱,这里的add操作触发了一次自动装箱操作,将int转化为Integer;接着从ArrayList里面获取元素,由于我们的目标变量类型是基本类型int,但获取到的元素类型是Integer,所以编译器在这里帮我们做了拆箱的操作。

通过字节码查看自动装箱和自动拆箱是如何实现的

我们经常说自动装箱、自动拆箱,到底是如何个自动法,我们来一个眼见为实,通过查看java代码生成的字节码来看下编译器对我们的代码做了什么。

查看字节码的方式

这里介绍两种查看字节码的方式:

  • 第一种,通过javac和javap查看:先通过javac将.java代码编译成.class字节码,然后通过javap分析字节码。
(base) tinytongtongdeMacBook-Pro% javac TestAutoWrapper.java
(base) tinytongtongdeMacBook-Pro% javap -verbose TestAutoWrapper

这样你就能看到你的字节码信息了。

  • 第二种,通过IDE插件ASM Bytecode Outline来查看,具体操作方式见插件说明。

查看自动装箱和拆箱的字节码

public static void main(String[] args) {
    // int类型的自动装箱和拆箱
    ArrayList<Integer> integerArrayList = new ArrayList<>();
    integerArrayList.add(1);// 自动装箱
    int i = integerArrayList.get(0);// 自动拆箱
}

我们生成这段java代码的字节码,核心部分如下:

// access flags 0x9
  public static main([Ljava/lang/String;)V
   ...
   L1
    LINENUMBER 15 L1
    ALOAD 1
    ICONST_1
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    INVOKEVIRTUAL java/util/ArrayList.add (Ljava/lang/Object;)Z
    POP
   L2
    LINENUMBER 16 L2
    ALOAD 1
    ICONST_0
    INVOKEVIRTUAL java/util/ArrayList.get (I)Ljava/lang/Object;
    CHECKCAST java/lang/Integer
    INVOKEVIRTUAL java/lang/Integer.intValue ()I
    ISTORE 2
   ...

L1部分中的倒数第二行,INVOKEVIRTUAL java/util/ArrayList.add (Ljava/lang/Object;)Z,INVOKEVIRTUAL指令表示一个虚方法调用,这里具体就是我们java代码中的integerArrayList.add(1);,自动装箱发生在哪呢?就在它上面,INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;,INVOKESTATIC表示静态方法调用,这里对应的Java语句就是Integer#valueOf()方法。

接下来我们看下自动装箱对应的字节码,也就是L2部分,先看INVOKEVIRTUAL java/util/ArrayList.get (I)Ljava/lang/Object;,它对应的java代码是integerArrayList.get(0),表示从ArrayList里面获取到Integer类型对象。自动拆箱发生在下面,就是NVOKEVIRTUAL java/lang/Integer.intValue ()I这条指令,它对应的java方法是Integer#intValue()方法。

看到这里相信大家对自动装箱和拆箱有一个比较具体的认识了,说白了就是编译器会根据情况替我们做一些工作,通过插入字节码指令来替我们完成装箱和拆箱操作。int对应的装箱方法是Integer#valueOf,拆箱方法是Integer#intValue()。

自动装箱和拆箱的触发时机

我们接着讲下自动装箱和拆箱的触发时机,具体如下:

* 进行 = 赋值操作(装箱或拆箱)
* 进行+,-,*,/混合运算 (拆箱)
* 进行>,<,==比较运算(拆箱)
* 调用equals进行比较(装箱)
* ArrayList,HashMap等集合类 添加基础类型数据时(装箱)

基本类型自动装箱和拆箱方法总结

1

感兴趣的同学可以自己试下各种操作下,自动装箱和拆箱的表现,查看对应的字节码即可。

【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/live

立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

原文发布时间:2020-08-04
本文作者:tinyvampirepudge
本文来自:“掘金”,了解相关信息可以关注“掘金”

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!