跳到主要内容

Java深耕之自动装箱拆箱机制

· 8 分钟阅读
CheverJohn

自动装箱与拆箱的定义

装箱就是自动将基本数据类型转换为包装器类型;拆箱就是,自动将包装器类型转换为基本数据类型

Java中的数据类型分为两类:一类是基本数据类型,另一类是引用数据类型

基本数据类型的分类.jpg

简单类型二进制位数封装器类
int32Integer
byte8Byte
long64Long
float32Float
double64double
char16Character
boolean1Boolean

上自动装箱代码

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)方法简化后的代码:

public static Integer valueOf(int i) {       
if (i >= -128 && i <= 127)
return IntegerCache.cache[i + 127];
//如果i的值大于-128小于127则返回一个缓冲区中的一个Integer对象
return new Integer(i);
//否则返回 new 一个Integer 对象
}

可以看到Integer.valueOf(a)其实是返回了一个Integer的对象。因此由于自动装箱的存在Integer b = a这段代码是没有问题的,并且我们可以简化的来这样写: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值嘛,但是这里的value又是怎么一回事呢?继续找源码:

public Integer(int value) {
this.value = value;
}

原来这里的value就是,Integer后边括号里的值呀,于是我们的拆箱代码其实本质上是这样写的:

public static void main(String[] args) {
// TODO Auto-generated method stub

Integer b=new Integer(3);
//b为Integer的对象
int a=b.intValue();
//其中b.intValue()返回实例化b时构造函数new Integer(3);赋的值3。
System.out.println(a);
//打印输出3。
}

范围概念

这里边是一个挺重要的知识点,至少我之前看的疯狂Java视频资料,以及我看的《Java编程思想》这本书,都有这方面的介绍。先看一个代码哈:

public static void main(String[] args) {
Integer a = 1000,b=1000;
Integer c=100,d=100;
System.out.println(a==b);
System.out.println(c==d);
}

原本我会以为是输出的是:true true啦,但是实际上不对,正确答案是false true。为甚呢?细细道来。

public static void main(String[] args) {        
//1
Integer a=new Integer(123);
Integer b=new Integer(123);
System.out.println(a==b);//输出 false

//2
Integer c=123;
Integer d=123;
System.out.println(c==d);//输出 true

//3
Integer e=129;
Integer f=129;
System.out.println(e==f);//输出 false
//4
int g=59;
Integer h=new Integer(59);
System.out.println(g==h);//输出 true
}

常量池.jpg

第一部分输出false,很好理解,因为比较的是堆中指向的对象是不是同一个嘛,a,b是栈中对象的引用分别指向堆中的两个不同的对象。而a==b这条语句就是判断a、b在堆中指向的对象是不是统一个,因此输出为false。

第二部分输出true也很好理解,正是用了我们的自动装箱技术

我带大家这次仔细的看自动装箱的源码

public static Integer valueOf(int i) {       
if (i >= -128 && i <= 127)
return IntegerCache.cache[i + 127];
//如果i的值大于-128小于127则返回一个缓冲区中的一个Integer对象
return new Integer(i);
//否则返回 new 一个Integer 对象
}

上面的代码中:IntegerCache.cache[i + 127]; 表示狠眼生,继续看代码:

 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);
//否则返回一个新的对象。
}
}

原来IntegerCache类在初始化的时候,生成了一个大小为256的integer类型的常量池,并且integer.val的值从-128~127,当我们运行Integer c = a(临时做的一个小栗子哈)的时候,如果-128 <= a <= 127,就不会再生成新的integer对象。于是我们第二部分的c和d指向的是同一个对象,所以比较的时候是相等的,所以我们输出true。

第三部分,理解如第二部分

第四部分:代码中g指向的是栈中的变量,h指向的是堆中的对象,但是我们的g == h为什么还是true呢?这就是自动插箱干的好事,g == h这代码执行的时候就是:g == h.IntValue(),而h.IntValue()=59,所以两边其实是两个int在比较而已。

总结

简单一句话:

装箱就是自动将基本数据类型转换为包装器类型;

拆箱就是自动将包装器类型转换为基本数据类型。