在一名 C++ 程序员看来,Java 中不少名词听着高大上,实则很是简单,装箱和拆箱尤甚。来看下装箱和拆箱的定义:java
装箱是指将基本类型转化成包装器类型,拆箱是指将包装器类型转化成基本数据类型。程序员
Java 有八种基本数据类型 byte/char/boolean/short/int/long/float/double 。在一切皆对象的 Java 世界里,固然也少不了它们的对象包装类型 Byte/Character/Boolean/Short/Integer/Long/Float/Double。用一张图表达它们的关系最简单不过:数组
public class BoxTest {
public static void main(String[] args) {
Long objectLong100 = 100L;
long baseLong100 = objectLong100;
}
}
复制代码
其中Long objectLong100 = 100L;
对 100L 进行装箱成 Long 类型并赋给 objectLong100 对象类型变量,同理long baseLong100 = objectLong100;
对objectLong100 进行了拆箱并赋值给基本类型变量 baseLong100。缓存
从示例代码 1 能够看到基本类型和包装类型之间的转换可自动进行。用 javap 反编译上述代码可看到以下字节码: post
从字节码能够看出,示例代码 1 被编译器改写成以下形式:spa
public class BoxTest {
public static void main(String[] args) {
Long objectLong100 = Long.valueOf(100L);
long baseLong100 = objectLong100.longValue();
}
}
复制代码
这就是 Java 语法糖之一的(Java 语法糖还有不少,可参考《Hollis原创|不了解这12个语法糖,别说你会Java》)---自动装箱和拆箱,它省去了程序员显示的用代码在基本类型和包装器类型之间进行转化。code
自动装箱的情景比较单一:将基本类型赋值给包装器类型的时候,这里的赋值不必定是 =/-=/+= 符号,也能够是方法调用 都会触发自动装箱。以下:cdn
public class BoxTest {
public static void main(String[] args) {
//= 赋值自动装箱
Long objectLong = 100L;
//equals 调用自动装箱
if (objectLong.equals(100L)) {
System.out.println("objectLong.equals(100L)");
}
//add 方法调用自动装箱
List<Long> objectLongList = new ArrayList<>();
objectLongList.add(objectLong);
}
}
复制代码
同理,将包装器类型赋值给基本类型的时候会发生自动拆箱:对象
public class BoxTest {
public static void unpacking(long var) {
System.out.println("var = " + var);
}
public static void main(String[] args) {
Long objectLong = 100L;
//= 赋值自动拆箱
long basicLong = objectLong;
//方法调用自动拆箱
unpacking(objectLong);
}
}
复制代码
但与自动装箱不一样,自动拆箱还会在以下状况发生:blog
一、当包装器类型之间或者包装器类型与基本类型之间进行数学运算(+、-、*、/、++、--等)的时候。
二、当包装器类型与基本类型之间进行 ==、!= 比较操做时候。
其中 1 很好理解,毕竟数学运算(想一想 CPU 操做)只能在基本类型之间进行而不能在对象之间进行。2 举个例子来简单说明:
public class BoxTest {
public static void main(String[] args) {
Long objectLong = 500L;
long basicLong = 500L;
if (objectLong == basicLong) {
System.out.println("objectLong == basicLong");
} else {
System.out.println("objectLong != basicLong");
}
}
}
复制代码
执行输出objectLong == basicLong
,这是在基本类型与包装器类型之间进行比较,发生了拆箱操做,因此输出相等。若是仍是觉得 basicLong 会被装箱成包装器类型再与 objectLong 比较,那就大错特错了。
自动装箱和拆箱大法好,可是其中也存在不少"陷阱",且听我抽丝剥茧,一一道来。来看一段代码:
public class BoxTest {
public static void main(String[] args) {
Long objectLong1 = new Long(10);
Long objectLong2 = new Long(10);
Long objectLong11 = 10L;
Long objectLong22 = 10L;
Long objectLong5 = 1000L;
Long objectLong6 = 1000L;
Long objectLong3 = new Long(10);
long basicLong = 10L;
if(objectLong1 == objectLong2) {
System.out.println("objectLong1 == objectLong2");
} else {
System.out.println("objectLong1 != objectLong2");
}
if(objectLong11 == objectLong22) {
System.out.println("objectLong11 == objectLong22");
} else {
System.out.println("objectLong11 != objectLong22");
}
if(objectLong5 == objectLong6) {
System.out.println("objectLong1 == objectLong2");
} else {
System.out.println("objectLong1 != objectLong2");
}
if (objectLong3 == basicLong) {
System.out.println("objectLong3 == basicLong");
} else {
System.out.println("objectLong3 != basicLong");
}
}
}
复制代码
先用笔写下你认为的输出的结果,再看我下面截图中的输出,看你"执行"的对不对。运行结果以下:
一、objectLong1 与 objectLong2 的比较结果不出意外,由于他们是两个包装器类型变量。因为它们并非同一个对象,因此并不相等。
二、objectLong3 与 basicLong 的比较根据什么时候进行自动装箱和拆箱讲到的拆箱的场景也能够快速理解。
三、objectLong5 与 objectLong6 比较不相等大部分人还能推理的到:objectLong5 与 objectLong6 都发生自动装箱成包装器对象,两个对象之间的 == 比较并不相等。但 objectLong11 与 objectLong22 的比较输出结果恐怕要让不少人大跌眼镜。 所以咱们来看下 Long 自动装箱时调用的方法 Long.valueOf(long value)
的源代码:
public final class Long extends Number implements Comparable<Long> {
//......此处省略一堆
private static class LongCache {
private LongCache(){}
static final Long cache[] = new Long[-(-128) + 127 + 1];
static {
for(int I = 0; I < cache.length; I++)
cache[I] = new Long(I - 128);
}
}
//......此处省略一堆
public static Long valueOf(long l) {
final int offset = 128;
if (l >= -128 && l <= 127) { // will cache
return LongCache.cache[(int)l + offset];
}
return new Long(l);
}
//......此处省略一堆
}
复制代码
能够看出当要进行自动装箱的基本类型值在 [-128~127] 之间的时候,直接将 LongCache 中缓存的对象返回,而 LongCache.cache 是一个数组,里面保存了值为 -128~127 的包装器对象,在 Long.class 第一次加载的时候分配。 这就解释了为何 10L 自动装箱成两个对象,他们之间是相等的。而 1000L 自动装箱成两个对象却不相等。前者用的是 LongCache.cache 中缓存中的同一个对象,后者每次自动装箱都 new 一个新对象。
其余包装器类型在装箱时都采用了相似的缓存方法,防止生成太多对象,但 Float 和 Double 例外。自动装箱方法大致上分为如下三种:
感兴趣的能够点进包装类源码中进行查看,比较简单。
一、包装器类型本质上是一个对象,自动装箱时先在堆中 new 一个对象,再将基本类型的值赋给该对象中的 value。
二、必定不要使用 ==/!= 在包装器类型之间、包装器与基本类型之间进行比较。请使用 equals 方法。
三、当进行数学运算时,遇到包装器类型会进行自动拆箱,再对基本类型进行运算。