自动装箱和自动拆箱
Java 提供了 8 种基本数据类型,每种数据类型都有其对应的包装类型,包装类是面向对象的类,是一种高级的数据类型,可以进行一些比较复杂的操作,它们是引用类型而不再基本类型了。
基本类型和包装类型的对应关系| 基本类型 | 包装类型 |
|---|---|
| byte(1字节 | Byte |
| short(2字节) | Short |
| int(4字节) | Integer |
| long(8字节) | Long |
| float(4字节) | Float |
| double(8字节) | Double |
| char(2字节) | Character |
| boolean(1字节) | Boolean |
自动装箱
自动装箱,简单来说,就把基本数据类型自动包装为引用类型,
在 JDK 1.5 之前,是不能把一个基本类型直接赋值给引用类型的,需要引用类型,就必须使用创建对象的方式,如下:
Integer integer = new Integer(10);
JDK 1.5 之后,它们之间可以自由的转换,如下所示:
int j = 20;
Integer integer1 = j;
JVM 会把基本类型 j 自动包装为引用类型 Integer,这就是自动装箱;查看下它的class文件,如下:
int j = 20;
Integer integer1 = Integer.valueOf(j);
可以看到,自动装箱,就是调用 valueOf() 方法来实现的。
自动拆箱
自动拆箱,就是把一个引用类型自动转换为基本类型,如下所示:
Integer integer = new Integer(10);
int i = integer;
反编译它的class文件如下:
Integer integer = new Integer(10);
int i = integer.intValue();
可以看到,自动拆箱就是调用 intValue() 方法来实现的。
上述完整代码如下:
public class Hello {
public static void main(String[] args) {
Integer integer = new Integer(10);
int i = integer;
int j = 20;
Integer integer1 = j;
}
}
反编译class文件如下:
public class Hello
{
public static void main(String[] args)
{
Integer integer = new Integer(10);
int i = integer.intValue();
int j = 20;
Integer integer1 = Integer.valueOf(j);
}
}
还有一点需要注意的是,当在调用 valueOf() 方法的时候,默认情况下,如果需要转换的数值在 -128~127 之间,则直接从缓存中取,而不会创建 Integer 对象,源码如下:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
接下来看下它的静态内部类 IntegerCache,它在第一次使用的时候进行加载,上面说了,默认情况下在 -128~127 之间从缓存读取,这个最大值 127 是可以配置的,通过虚拟机参数 -XX:AutoBoxCacheMax=<size> 进行配置,它的源码如下:
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) {
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
接下来验证一下:
默认情况下:
Integer i1 = Integer.valueOf(-20);
Integer i2 = Integer.valueOf(-20);
System.out.println(i1 == i2); // true
Integer i3 = Integer.valueOf(20);
Integer i4 = Integer.valueOf(20);
System.out.println(i3 == i4); // true
Integer i5 = Integer.valueOf(127);
Integer i6 = Integer.valueOf(127);
System.out.println(i5 == i6); // true
Integer i7 = Integer.valueOf(-129);
Integer i8 = Integer.valueOf(-129);
System.out.println(i7 == i8); // false
Integer i9 = Integer.valueOf(128);
Integer i10 = Integer.valueOf(128);
System.out.println(i9 == i10); // false
可以看到,在 -128~127 之外,则会创建新的对象,在之间则会从缓存中读取。
接下来修改这个上限值,通过添加虚拟机参数 -XX:AutoBoxCacheMax=<size> 把上限值设置为 200
在运行上面的用例:
Integer i1 = Integer.valueOf(128);
Integer i2 = Integer.valueOf(128);
System.out.println(i1 == i2); // true
Integer i3 = Integer.valueOf(200);
Integer i4 = Integer.valueOf(200);
System.out.println(i3 == i4); // true
Integer i5 = Integer.valueOf(201);
Integer i6 = Integer.valueOf(201);
System.out.println(i5 == i6); // false
可以看到,上限值已经改变了。
Integer、Short、Byte、Character、Long 的 valueOf 方法都是和上面一样,在 -128~127 之间之间从缓存从取。
Double,Float 则不是,因为在某个范围内的整型数值的个数是有限的,而浮点数却不是
Double l1 = Double.valueOf(100.0);
Double l2 = Double.valueOf(100.0);
System.out.println(l1 == l2); //false
Double l3 = Double.valueOf(200.0);
Double l4 = Double.valueOf(200.0);
System.out.println(l3 == l4);//false
它们的 valueOf 方法如下:
public static Double valueOf(double d) {
return new Double(d);
}
public static Float valueOf(String s) throws NumberFormatException {
return new Float(parseFloat(s));
}
Boolean 类型又是如何呢?
Boolean b1 = Boolean.valueOf(true);
Boolean b2 = Boolean.valueOf(true);
System.out.println(b1 == b2);//true
Boolean b3 = Boolean.valueOf(false);
Boolean b4 = Boolean.valueOf(false);
System.out.println(b3 == b4);//true
它的源码如下:
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
可以看到,它们指向的是同一个静态对象。
总结
1.自动装箱:把基本类自动转换为引用类型,调用的是 valueOf() 方法实现
2.自动拆箱:把引用类型自动转换为基本类型,调用的是 intValue() 方法实现
3.valueOf() 方法:Integer、Short、Byte、Character、Long 的 valueOf() 方法,在一定数值范围内直接从缓存中获取,而不会创建对象,只有超过了界限才会创建对象。Double,Float 的 valueOf() 方法每次都会创建新的对象,因为在一定范围内浮点数的个数是无限的。Boolean 对象的 valueOf() 方法调用的都是同一个静态对象。