装箱与拆箱
JAVA中的自动装箱指的是把基本类型
的值转换为
对应的包装类对象
,自动拆箱则相反。如下代码所示:
Integer i1=100;
int i2=i1;
- 分析如下:
- Integer i1=100实现了
自动装箱
,底层调用了调用包装类的valueOf方法,也就是Integer.valueOf(100)
。 - int i2=i1实现了
自动拆箱
,底层调用包装类的intValue
方法。
需要注意的是:自动装箱拆箱是 编译器 帮我们自动转换的,我们不需要手工调用valueOf()和intValue()方法。
Integer的缓存 -128~127
- 自动装箱时的缓存问题:
先看一个问题,对于下面定义的四个变量进行==比较:
Integer a=1;
Integer b=1;
Integer c=200;
Integer d=200;
System.out.println(a==b);//true
System.out.println(c==d);//false
我们都知道“= =”在JAVA里面是比较对象引用的,如果两个对象引用指向堆中的同一块内存就返回true,否则返回false。根据自动装箱规则我们知道Integer a = 1 等价于
Integer a = Integer.valueOf(1);,但是在valueOf方法上,查看源码:
public static Integer valueOf(int i) {
final int offset = 128;
if (i >= -128 && i <= 127) { // must cache
return IntegerCache.cache[i + offset];
}
return new Integer(i);
}
Integer.valueOf()中有个内部类IntegerCache(类似于一个常量数组
,也叫对象池
),它维护了一个Integer数组cache,长度为(128+127+1)=256。Integer类中还有一个Static Block(静态块)
static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Integer(i - 128);
}
从这个静态块可以看出,Integer已经默认创建了数值[-128,-127]
的Integer缓存数据。所以使用Integer a=1时,JVM会直接在该在对象池找到该值的引用
。也就是说这种方式声明一个Integer对象时,JVM首先会在Integer对象的缓存池中查找
有木有值为1的对象,如果有直接返回该对象的引用
;如果没有
,则使用New Integer创建一个对象
,并返回该对象的引用地址
。因为Java中==
比较的是两个对象是否是同一个引用
(即比较内存地址),a和b都是引用的同一个对象,所以a= =b结果为true;c和d已经超出了缓存的范围,所以重新生成了Integer对象,所以c==d结果为false。
int和Integer的比较== equals()
- 示例代码:
class BoxIntInteger {
public static void main(String[] args) {
Integer a = new Integer(10111);
int b = 10111;
boolean equal1 = a == b;
boolean equal2 = a.equals(b);
System.out.println(equal1);
System.out.println(equal2);
}
}
运行结果:
true
true
- 反编译字节码:
public static void main(String args[])
{
Integer a = new Integer(10111);
int b = 10111;
boolean equal1 = a.intValue() == b;
boolean equal2 = a.equals(Integer.valueOf(b));
System.out.println(equal1);
System.out.println(equal2);
}
- 由实例代码和反编译代码可以看出:
-
可以看出对于Integer与int使用 = = 比较大小的话,优先Integer拆箱。
-
如果使用equals比较大小的话,则int装箱。
提示:对于Integer与int之间大小比较优先使用equals比较,否则容易出现空指针,例如:
Integer c= null;
System.out.println(c==1);
原因:由于Integer需要调用intValue进行拆箱
,因而空指针
。
Integer与Integer必须使用equals方法比较
,这个就不必解释了。但是通常我们可以看先Integer与Integer之间使用==也可以正确比较
,原因是:Integer对于-128到127之间的数字在缓存中拿,不是创建新对象。
为什么要用到包装类型?
- 把一个基本类型包装成一个类,
可以使这个类型具有很多可以调用的方法
。 - 包装类型
使得Java向面向对象语言的靠近
。其实Java还不算是很纯的面向对象的语言。真正的面向对象,是没有基本数据类型的
。它只有一种类型,就是对象。 - 在泛型中,
基本类型是不可以做泛型参数的
。如:List < int> list = new ArrayList< int> ();这是不合法的
。你只能这个样写List< Integer> list = new ArrayList< Integer> ();也就是要用int型的包装类类型来解决基本类型不可以做泛型参数的问题 。