目标
了解什么是装箱和拆箱
掌握自动拆装箱机制原理
了解缓存池
初步了解
- 装箱:基本数据类型——>包装数据类型(与基本数据类型对应的引用数类型)
- 拆箱:包装类型——>基本数据类型
- 从JDK1.5开始提供自动拆装箱机制,JDK1.5之前都是需要手动进行
自动装箱
// 自动装箱机制
int a = 5;
Integer b = a;
// 或
Integer b = new Integer(a);
装箱的本质
上面代码中基本数据类型的变量a赋值给了引用数据类型b,这在传统上的认知中是不可能的。
JDK1.4.2
// 返回一个 Integer对象,保存指定的值为 String 。
public static Integer valueOf(String s) throws NumberFormatException {
return Integer.valueOf(parseInt(s, 10));
}
// 返回一个 Integer对象,保存从指定的String中 String的值,当用第二个参数给出的基数进行解析时。
public static Integer valueOf(String s, int radix) throws NumberFormatException {
return Integer.valueOf(parseInt(s,radix));
}
JDK1.5开始
// 返回一个 Integer指定的 int值的 Integer实例。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
本人经过对比JDK1.4.2与JDK1.5.0的API文档,发现在JDK1.4.2中Byte,Short,Integer,Long,Character
这五个包装类是不存在valueOf(int i)
方法的,JDK1.5.0中发现这五个包装类的方法说明中都多出一个valueOf(int i)
方法。
同时JDK1.5才开始提供自动拆装箱机制,由此,可以说明Byte,Short,Integer,Long,Character
这五个包装类的拆装箱机制是依赖方法valueOf(int i)
实现的。
当然,Double
的自动拆装箱机制也是通过valueOf
来实现的。Boolean
在JDK1.4就存在与之相对应的valueOf
,但是我更偏向于Boolean不存在自动拆装箱机制。
小结
-
装箱的本质是通过方法
valueOf
来实现的 -
为什么是自动呢?
个人认为,
int a=5; Integer b = a;
中Integer b = a
自动调用了valueOf
方法
方法valueOf
通过查看源码发现,Byte,Short,Integer,Long
与Character
的valueOf
方法有些出入,实现原理一致,但是缓存池数据范围不一致。
Byte,Short,Integer,Long
的缓存池范围是byte
的数据范围,即[-128,127]
/**
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
Character
的缓存池范围是[0,127]
/**
* This method will always cache values in the range {@code
* '\u005Cu0000'} to {@code '\u005Cu007F'}, inclusive, and may
* cache other values outside of this range.
*
* @param c a char value.
* @return a <tt>Character</tt> instance representing <tt>c</tt>.
* @since 1.5
*/
public static Character valueOf(char c) {
if (c <= 127) { // must cache
return CharacterCache.cache[(int)c];
}
return new Character(c);
}
对于Double
,也是在JDK1.5开始引入了自动拆装箱机制,不过与Byte,Short,Integer,Long,Character
不同的是,Double
中的valueOf
方法不存在缓存池,永远都是创建一个新的对象
/**
* @param d a double value.
* @return a {@code Double} instance representing {@code d}.
* @since 1.5
*/
public static Double valueOf(double d) {
return new Double(d);
}
自动拆箱
Integer inte = 2;
int c = inte;
// 本质
int c = inte.intValue();
本质是使用了intValue()
/**
* Returns the value of this {@code Integer} as an
* {@code int}.
*/
public int intValue() {
return value;
}
缓存池
缓存池可以与String中的字符串常量池做类比,当定义Integer对象时,会先去缓存池中查找是否有对应的对象,如果有就直接引用,不会去创建新的对象;如果没有,才会创建新的对象。
- 例子
// 缓存范围:[-128,127]
Integer num1 = 100;
Integer num2 = 100;
Integer num3 = 200;
Integer num4 = 200;
System.out.println(num1 == num2);
System.out.println(num3 == num4);
Integer num5 = 200;
int num6 = 200;
//Integer与int比较的时候,Integer会自动拆箱转换为int类型
System.out.println(num5 == num6);
- 答案
//true 自动装箱,从缓存池中直接引用,没有创建对象,直接比较字面值
//false 自动装箱,超过缓存池范围,创建对象,所以num3和num4都是指向各自的对象地址,比较对象地址
//true 比较的是字面值
- 例子
Double d1 = 1.23;
Double d2 = 1.23;
System.out.println(d1 == d2);
System.out.println(d1.equals(d2));
- 答案
//false 自动装箱,永远都是创建各自的Double对象
//true 比较字面值
包装类型与基本类型性能比较
// int与Integer性能
long start = System.currentTimeMillis();
int p = 0;
for(int i=0; i<100000; i++){
p += i;
}
long end = System.currentTimeMillis();
System.out.println(end-start); // 0ms
long start1 = System.currentTimeMillis();
Integer p1 = 0;
for(int i=0; i<100000; i++){
p1 += i;
}
long end1 = System.currentTimeMillis();
System.out.println(end1-start1); // 31ms
当数据量大的时候,对于Integer
来说执行效率上会慢一些,因为当超过[-128,127],需要创建Integer实例,比较耗费时间。
总结
自动装箱
-
存在缓存池
-
对于
Byte,Short,Integer,Long
来说,它们的缓存池范围都是[-128,127]
,即一个byte
的数据范围 -
对于
Character
而言,它的缓存池范围{@code '\u005Cu0000'} to {@code '\u005Cu007F'}
转换为int
来说是[0,127]
-
-
不存在缓存池
-
对于
Double
而言,它不存在缓存池,当使用valueOf
进行装箱的时候永远都是创建一个新的Double
实例 -
对于
Boolean
而言,也是使用valueOf
方法。(不了解它,不过就false和true两个值)
-
自动装箱本质
是通过方法valueOf
,以Integer
为例,该方法通常是会先去缓存池中查看请求的值存在对应的值(未超过范围),若是请求的值超过缓存范围,则调用构造器Integer(int)
创建一个新的实例。当然前提是包装类存在缓存池,否则如Double
调用valueOf
方法永远都是创建一个新实例。
自动拆箱本质
是通过包装类中各自已经存在的方法,形式基本数据类型Value()
,例如Integer的intValue()
、Double的doubleValue()
如何选择使用
若是需要使用对象,则使用包装类,若无需使用到对象,则使用基本数据类型即可