云栖号资讯:【点击查看更多行业资讯】
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!
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等集合类 添加基础类型数据时(装箱)
基本类型自动装箱和拆箱方法总结
感兴趣的同学可以自己试下各种操作下,自动装箱和拆箱的表现,查看对应的字节码即可。
【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/live立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK
原文发布时间:2020-08-04
本文作者:tinyvampirepudge
本文来自:“掘金”,了解相关信息可以关注“掘金”
来源:oschina
链接:https://my.oschina.net/u/4312161/blog/4474353