个人网站地址:
目录
包装类概述
什么是包装类?
基本数据类型的强化版,你可以把基本数据类型理解为 托尼 · 斯塔克,包装类理解为钢铁侠。
基本数据类型 | 对应包装类 |
byte | Byte |
char | Character |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
为什么要使用包装类?
众所周知,在JAVA里万事万物皆为对象,但是8种基本数据类型似乎鹤立鸡群,它们能完成的功能很少,所以JAVA类库提供了包装类。如果学到后面的集合你就会发现,集合都不支持基本数据类型。
又比如,你想知道int类型的最大值具体是多少,难道还要去用计算器算吗?直接使用Integer.MAX_VALUE 就可以获取你想要的。它还有更多功能,将十进制转换为二进制、八进制、十六进制、将n进制转换成十进制.....
包装类共性
1、所有包装类都没有无参构造函数
2、所有包装类的底层值都不可变
3、每一个包装类都有静态常量:MAX_VALUE 、MIN_VALUE (Boolean除外)
4、所有包装类都提供了装箱方法:valueOf()。拆箱方法:类型value()
共性1:包装类没有无参构造函数
共性2:包装类的底层值都不可变
共性3:每一个包装类都有静态常量:MAX_VALUE 、MIN_VALUE(Boolean除外)
共性4、所有包装类都提供了装箱方法:valueOf()。拆箱方法:类型value()
Integer I = new Integer(5); // 装箱
int i = I.intValue(); // 拆箱
Double D = new Double(5.2); // 装箱
double d = D.doubleValue(); // 拆箱
装箱、拆箱
什么是装箱、拆箱?
装箱 :将 int 转换为 Integer,也就是把int包装起来
拆箱 :将 Integer 转换为 int,也就是把Integer褪下包装
装箱
装箱 :将 int 转换为 Integer
方法一:使用构造函数
new Integer(int value);
new Integer(String s);
看,输出i,直接输出了93,也就是说包装类重写了toString()方法,能够直接输出它底层的值。
方法二:使用静态方法
Integer.valueOf(int value);
Integer.valueOf(String s);
拆箱
拆箱 :将 Integer 转换为 int,也就是把Integer褪下包装
使用方法 类型Value() (这个不是静态方法哦,要与装箱区分开)
Integer I = new Integer(10); // 装箱
int i = I.intValue(); // 拆箱
Character C = new Character('h'); // 拆箱
char c = C.charValue(); // 拆箱
Double D = new Double(5.2); // 装箱
double d = D.doubleValue(); // 拆箱
有人说parseInt()也可以理解为拆箱,parseInt()是将一个Integer对象或者字符串转换成int值。这个看你要怎么想了。
Integer I = new Integer(10);
int i = I.parseInt();
这里要重点讲一下valueOf() 这个方法,为更好理解自动装箱/拆箱铺垫。
经过刚才的学习,我们已经知道了valueOf是将一个int值装换成Integer值,那么他的原理是什么呢?
我们可以查看它的源码:
可以看到,如果没有触发一开始的if:
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
这个条件的话,valueOf()的底层其实还是调用了new Integer(i)来new一个对象。
那么这个奇奇怪怪的条件是什么呢?留一个悬念。下面会讲
自动装箱、自动拆箱
由于包装类实在是太常用太常用了,我们难道要一直new Integer对象或者打出Integer.valueOf()这样冗余的代码吗?
为了简化这个过程,让包装类更加容易上手,JAVA工程师在设计包装类时就已经做了优化。
Integer I = 5;
Double D = 1.0;
Boolean B = true;
是不是比刚才那些繁琐的代码轻松了很多?
那么如此轻松的代码是如何实现的呢?
其实他的本质还是调用了Integer.valueOf() ,我们可以使用反编译工具XJad查看
(如果你没有反编译工具或者不会使用它,可以借鉴:JAVA查看反编译代码)
Integer I = 5;
可以看到,自动装箱时,默认调用Integer.valueOf() 方法,不用我们辛辛苦苦去写了。
接下来看自动拆箱
Integer I = 5; // 自动装箱
int i = I; // 自动拆箱
我们依旧使用XJad查看
看,他其实也是调用了intValue()方法。
所以说,自动装箱、拆箱与手动装箱拆箱的差别只有:更加方便。
有了基础,可以看看这些练习题了:
// 手动装箱
1、Integer A = new Integer("23");
2、Integer B = Integer.valueOf("23");
3、Integer C = Integer.parseInt("23");
4、Double D = new Double();
5、Double E = Double.valueOf("23.4");
6、int i = (Integer.valueOf("23")).intValue();
7、double d = (Double.valueOf("23.4")).doubleValue();
// 自动装箱
8、Integer x = 3 + new Integer(5);
9、int x = new Integer(3) + new Integer(4);
10、Double x = 3;
有些题很简单,挑一些难的浅讲一下
3、Integer C = Integer.parseInt("23"); 将"23"转换为int值,再将int值自动装箱。
4、Double D = new Double(); 报错,包装类没有无参构造。
8、Integer x = 3 + new Integer(5); 构造一个Integer(5),再将它拆箱为(int)5,与3相加,得值(int)8 ,再将8自动装箱为Integer(8)。
9、int x = new Integer(3) + new Integer(4); 检测到符号:"+",将两侧的(Integer)3和(Integer)4自动拆箱为(int)3和(int)4,相加得值7。
10、(int)3装箱为Integer,发现要的是Double,所以报错。
使用XJad查看刚才第8题和第9题的分析:
缓存池
还记得刚刚遗留的问题吗?对,就是Integer.valueOf()下面的一大串条件IntegerCache巴拉巴拉的
在讲缓存池之前有一个问题:
Integer A = 100;
Integer B = 100;
Integer C = 200;
Integer D = 200;
A == B ?
C == D ?
A和B相等吗?C和D相等吗?跑一下。
奇怪的事情发生了,为什么A == B,而C != D呢?
这就是 缓存池。
由于包装类实在是太常用了,自动装箱仅仅简化了代码,但包装类底层的值是不可改变的,所以空间并没有少用,你每次使用同一个数都需要在堆区重复new空间。
所以JAVA工程师就推出了一个和String类节省空间类似的方法:缓存池,即IntegerCache。
可以看到IntegerCache是一个内部类,
成员变量有: low = -128,high, 和一个Integer数组cache
在你使用它之前,他其实已经做了很多东西:
把 -128 ~ 127 之间的数先new出来放到Integer数组cache[]中,当你要使用 -128 ~ 127时,直接在这个数组中拿给你,不再new空间。即:你即使用了一万次1,他始终是cache[]里的那个1,这样就能有效防止空间浪费。当然,不止Integer有缓存池,其他包装类也有缓存池。
让我们梳理一下思路(举例Integer):
自动装箱默认调用Integer.valueOf(),如果这个数在 -128 ~ 127之间,那么直接在缓存池中取,如果不在这个区间,就得重新new了。