一. 自动拆装箱机制
Java中常见的数据为引用类型中的对象,有些场景下,需要基本数据类型拥有对象的性质,因此需要将基本数据类型转换为包装器型。此时将基本数据类型转换成包装器类型的过程称之为装箱, 将包装器类型转换成基本数据类型的过程称之为拆箱。
二. 基本类型及其对应的包装器类型
基本类型 | 包装器类型 |
byte | Byte |
short | Short |
int | Integer |
float | Float |
double | Double |
long | Long |
char | Character |
boolean | Boolean |
三. 自动装拆箱原理
自动拆装箱时在Jdk1.5新增的功能特性。自动装拆箱的过程是由编译器完成的而不是Jvm完成的,在编译的过程中,是否完成拆箱和装箱的过程是由编译器进行判断的。
在Jdk 1.5中,Interger的操作上引入了一个新功能来节省内存和提高性能。整型对象通过使用相同的对象引用实现了缓存和重用,代码示例如下:
public static void main(String[] args) {
Integer a = 1000;
Integer b = 1000;
Integer c = 100;
Integer d = 100;
System.out.println("a == b is " + (a == b));
System.out.println(("c == d is " + (c == d)));
}
输出结果:
a == b is false
c == d is true
结果分析:
在Jdk中,装箱的过程都是通过valueOf()这个方法来实现的,Integer的valueOf()源码如下:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
//如果i的值大于-128小于127则返回一个缓冲区中的一个Integer对象
return IntegerCache.cache[i + (-IntegerCache.low)];
//否则返回 new 一个Integer 对象
return new Integer(i);
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
简化后的源码:
private static class IntegerCache {
static final Integer cache[];
//定义一个Integer类型的数组且数组不可变
static {
//利用静态代码块对数组进行初始化。
cache = new Integer[256];
int j = -128;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
//cache[]原来是一个Integer 类型的数组(也可以称为常量池),value 从-128到127,
public static Integer valueOf(int i) {
if (i >=-128 && i <= 127)
return IntegerCache.cache[i + (-IntegerCache.low)];
//如果装箱时值在-128到127之间,之间返回常量池中的已经初始化后的Integer对象。
return new Integer(i);
//否则返回一个新的对象。
}
}
通过阅读源码我们发现,当Integer类加载时,会在内部缓存中创建256个Integer对象常量池,区间范围为[-128,127],当valueOf(int i)这个方法在转换整数值区间为[-128,127]时,不会创建新的对象而是从内部缓存中获取,当转换不在这个区间内时就会new一个新的对象。
注意:适用于整数值区间[-128,127] ,同时只适合于自动装箱,使用构造函数创建对象不适用。
三. 自动装箱实现
代码如下:
public static void main(String[] args) {
// TODO Auto-generated method stub
int a=3;
//定义一个基本数据类型的变量a赋值3
Integer b=a;
//b是Integer 类定义的对象,直接用int 类型的a赋值
System.out.println(b);
//打印结果为3
}
在这里,利用自动装箱原理实现了将一个基本数据类型的变量赋值给一个该类型对应的包装类对象。实际上,Integer b = a的实现是调用了Integer.valueOf(int i)方法,即实际上是Integer b = Integer.valueOf(a)。可以看到Integer.valueOf ( a )其实是返回了一个Integer的对象,其实可以简化为:Integer b=3,同样这段代码等价于:Integer b=Integer.valueOf (3 ) 。
四. 自动拆箱的实现
public static void main(String[] args) {
// TODO Auto-generated method stub
Integer b=new Integer(3);
//b为Integer的对象
int a=b;
//a为一个int的基本数据类型
System.out.println(a);
//打印输出3。
}
在这里,利用自动拆箱原理实现了将一个包装类对象赋值给一个该对象对应的基本数据类型变量。实际上,int a = b的实现是调用了int a = b.intValue()。
补充:intValue()的源码解析
public int intValue() {
return value;
}
有关value的源码:
private final int value;
public Integer(int value) {
this.value = value;
}
从上述源码可得,value就是定义Integer b = new Integer(3)所赋予的值,故上述代码可以写成:
public static void main(String[] args) {
// TODO Auto-generated method stub
Integer b=new Integer(3);
//b为Integer的对象,b.intValue返回实例化b时构造函数new Integer(3)赋予的值3。
int a=b.intValue();
//a为一个int的基本数据类型
System.out.println(a);
//打印输出3。
}
五. 代码示例解析
public static void main(String[] args) {
//new关键字,对象与对象
Integer a=new Integer(126);
Integer b=new Integer(126);
System.out.println(a==b);
//由于引用a和引用b指向的是不同的地址空间,故输出false
//自动装箱及满足Integer缓存机制
Integer c=126;
Integer d=126;
System.out.println(c==d);
//由于c和d通过Integer.valueOf(126)转换,且126在(-128,127)区间内,所以引用c和引用d指向的是同一个缓存中的对象地址,故输出true
//自动装箱及不满足Integer缓存机制
Integer e=136;
Integer f=136;
System.out.println(e==f);
//由于c和d通过Integer.valueOf(136)转换,但是136不在在(-128,127)区间内,所以引用c和引用d指向的是各自通过new关键字构造出的对象地址,故输出false
//自动拆箱,intValue()方法
int g=59;
Integer h=new Integer(59);
System.out.println(g==h);
//由于自动拆箱,h通过intValue()与int g之间实现了转化等效,即g==h.intValue()==59,最终g和h都是59,故输出true
}