大家好,我是被白菜拱的猪。
一个热爱学习废寝忘食头悬梁锥刺股,痴迷于girl的潇洒从容淡然coding handsome boy。
假如你喜欢我的文字,欢迎关注公众号“放开这颗白菜让我来”。
自动装箱与拆箱
前言
最近在为日后的实习面试以及秋招做准备,在复习的过程中发现自己有很多东西没有掌握,尤其是基础的地方,很多概念还有细节上的东西没有去深入理解,再加上自己的懒惰拖延心中一直有此症结,其实很长时间之前就说要复习,结果一个月过去了还是没有行动,内心还无比的焦虑,每天愁啊愁啊,好菜,真的好菜,找不到工作可怎么办呢。
当然这都是没有用的,让我们告别焦虑,告别恐慌,一步一个脚印,接下来通过每日更新的方式打破现状,像银行螺丝钉看齐,每日搞定一个小知识点。
不积跬步,无以至千里;不积小流,无以成江海。告别曾经焦虑的自己,开始行动吧!
今天就来谈谈 Java 中的自动装箱与拆箱。
我将通过what、why、how三个方面也就是它是什么,为什么要这样做以及如何做进行介绍。学会知其然也要知其所以然。
what
什么是自动装箱,什么又是自动拆箱?曾经有个帅哥在编码的时候有这么一个疑问,int用着用着怎么突然冒出一个Integer。最近在学 JVM 的时候才逐渐解开心中的疑惑,像 char、byte、short、int、long、float、double、boolean称之为基本数据类型。像Character、Byte、Short、Integer、Long、Float、Double、Boolean以及像我们创建的类如Person ,他们都是一个类。要说什么数据类型,他们都是引用数据类型,前者我们又称作包装器类型,如Integer是int的包装器类型。
我们心中要把这个概念搞清,尤其是要分清楚int他们是一个类型,而Integer是一个类。类中呢则包含了属性、方法等内容。在很多地方他们都是要分别对待的。如基本数据类型在 JVM 中是存储在栈上的,而对象实例则是存储在堆上,栈只是存储这个对象的引用(reference)。又比如 == ,对于基本数据类型,比较的是他们的值,对于引用数据类型,则是比较对象存储的内存地址。
说了那么多,主要还是想把知识点进行融合,学会融会贯通,举一反三,更不要死记硬背。铺垫那么多,进入主题。
自动装箱就是自动将基本数据类型转化为包装器类型;
自动拆箱就是自动将包装器类型转化为基本数据类型。
why
我们知道在 Java 中,万事万物皆对象。我们操作更多的是对象,包装成一个类的目的是为了让我们更好的去操作他,我们看jdk的文档怎么说
Integer
类在对象中包装了一个基本类型int
的值。Integer
类型的对象包含一个int
类型的字段。此外,该类提供了多个方法,能在
int
类型和String
类型之间互相转换,还提供了处理int
类型时非常有用的其他一些常量和方法。
说白了就是里面有很多方法,方便我们的操作。像ArrayList他存储的都是对象,ArrayList< int >这样写则是错的。
how
他们是如何实现自动装箱和拆箱的呢?正好正在学习 JVM,针对下面代码,我们使用 javap 反编译一下看一下字节码文件的内容。
public class AutoBoxing {
public static void main(String[] args) {
Integer a = 1000;
int b = a + 1;
}
}
我们看到装箱拆箱的操作时编译期间自动完成的,装箱是调用了Integer.valueOf()方法,拆箱则是调用了Integer.intValue()方法。
假如我们int b = a + 1;换成Integer b = a + 1;再使用 javap命令反编译一下
我们看两张截图的差别,发现在进行算术运算之前,先进行了拆箱,运算了之后又进行了装箱。这说明最后进行运算的时候是要转化为基本数据类型的。没想到就这么一小行代码一个加号背后竟然有这么多东西。
下面我们就通过一个例子去了解valueOf。
public class AutoBoxing {
public static void main(String[] args) {
// Integer a = 1000;
// Integer b = a + 1;
Integer a = 100;
Integer b = 100;
Integer c = 200;
Integer d = 200;
System.out.println(a == b);
System.out.println(c == d);
System.out.println(a.equals(b));
System.out.println(c.equals(d));
}
}
刚开始,咦,200不就是等于200嘛,怎么运行结果是false呢?那为什么100等于100成立呢?200不等于200,那下面equals怎么又成立了呢?小伙伴是否也有这么多问号。
接下来我们就深入的去探究其中的本质,我们点开源码看看Integer中的ValueOf方法。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
我们发现在转换的过程中,不是直接创建一个Integer对象,而是return了一个缓存类中的数组中对应下标的元素。那我们点来 IntegerCache 这个类。
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
我们通过阅读源码发现,他实际上在 -128-127 这个范围内已经提前帮我们创好了对象,127 这个数值我们可以通过指令进行修改,默认是127,也就是说在这个范围内,我们多次创建,拿到的实际上是同一个对象。就跟线程池一样,起到复用的效果,提高了效率。所以想想为什么叫IntegerCache也有原因的。
好,那我们也就明白了,为什么 100 == 100 就是true, 200 == 200就是false了。 前面讲了 == 对于基础数据类型比较的是数值本身,对于引用类型比较的是对象的内存地址。在 -128 到127这个范围内使用的是同一个对象,所以内存地址一样,而不在这个范围内每次都是重新创建一个对象,那么对象的内存地址肯定不一样啦。
那为什么下面 equals 都是true呢。我们点开 equals 方法
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
首先判断的是是不是这个 Integer这个类型,不是直接返回false。然后在调用intValue方法进行拆箱,我们看 intValue(),很简单,直接返回这个数值。
public int intValue() {
return value;
}
拆箱过后也就是基本数据类型,用 =比较他们的值,200 == 200,那肯定相等啦。
小伙伴们,听我这么一番解释,是不是豁然开朗啦?
其他类型详解
Integer 我们了解的差不多了,那其他类型呢?
我们通过阅读源码可知:
-
Byte、Short、Long是跟 Integer 一样的,默认范围是 -128 到 127。
-
Character则是 0 到 127。因为它本身就没有负数。
-
Float、Double是没有这种缓存机制的,因为你想一想小说他的个数是不确定的,0-1中间就有很多很多很多数字。
-
Boolean则直接用两个对象表示,值为True,值为False。
最终杀技
最后我们通过一道题来判断自己是否完全掌握这个概念。
public static void main(String[] args) {
Integer a = 1;
Integer b = 2;
Integer c = 3;
Integer d = 3;
Integer e = 321;
Integer f = 321;
Long g = 3L;
Long h = 2L;
System.out.println(c==d); //true
System.out.println(e==f); //false
System.out.println(c==(a+b)); //true
System.out.println(c.equals(a+b)); //true
System.out.println(g==(a+b)); //true
System.out.println(g.equals(a+b)); //false
System.out.println(g.equals(a+h)); //true
}
我想看完这篇文章应该很轻松解决这些问题了吧。难的也应该是倒数第一个和倒数第二个,拆箱又装箱。把我一个原则,进行运算的是基础数据类型,equals是对象中的方法,所以两边都要是对象。
拿倒数第一个距离,a + h,首先拆箱,这时候要注意一个类型转换,a 是 int,h是 long,所以最后类型是 long,最后装箱的结果是 Long。 而倒数第二个自动装箱的类型是 Integer,Integer当然与Long类型的不相等啦。
总结
在学习的过程中,要学会抓住事物的本质,以不变应万变。天下武功林林总总,我最喜欢独孤九剑,无招胜有招。重剑无锋,大巧不工。