java装箱与拆箱的深入理解
2016-09-04 本文已影响517人
zivxia
Java有一个类型系统有两个部分组成,包含基本类型(byte、char、int、short、long、float、double、boolean)和引用类型。而基本类型则对应着各自的引用类型,称为装箱的基本类型。而引用类型对应着各自的基本类型,称为拆箱的基本类型。对应的类型为:(Byte、Character、Integer、Short、Long、Float、Double、Boolean)
下面一具体例子来说明装箱与拆箱
//java 1.5之前创建一个Integer对象
Integer i = new Integer(10);
//java 1.5之后有了装箱的特性,直接用下列方式生成一个Integer对象
//在这个过程中会将int 类型的10自动装箱成为Integer类型
Integer i = 10;
//拆箱 输出的值为20,这个过程中,会先将Integer类型的j自动拆箱为基本类型的10,最后完成运算
Integer j = new Integer(10);
int k = 10;
System.out.print(j+k);
下面来说说基本数据类型与其对应的引用类型的区别:
- 基本类型只有值,而装箱基本类型既具有值也具有它们对象的同一性(就是两个装箱的基本类型具有相同的值和不同的同一性(对象不一样))
- 基本类型只有功能完备的值,而每个装箱类型不仅具有完备的值还具有所有功能值之外的null。
- 基本类型通常比装箱基本类型更节省时间和空间。
下面来举一个《Effective java中》的一个例子进行分析
Comparator<Integer> comparator = new Comparator<Integer>()
{
public int compareTo(Integer first,Integer second)
{
return first > second ? 1:first == second ? 0 : -1;
}
}
//这两个Integer实例都表示相同的值42
Integer a = new Integer(42);
Integer b = new Integer(42);
int c = comparator.compareTo(a,b);
//返回值多少 0 ? 但实际结果却是-1,难道 42 < 42 ?
下面就来解释上面例子的原因:
首先执行 a>b,两个Integer都自动拆箱为42,不成立,则执行 a==b,乍一看,没什么问题,42==42没问题啊,但实际上这是违反了了装箱的同一性,因为a和b都是引用类型,根据上述两个装箱类型具有相同的值和不同的同一性,只是两个对象的比较,即这两个对象是不同对象,所对应的内存也不一样,a!=b。所有正确答案应该是 -1。
现在来说说装箱和拆箱是怎么实现的:
public static void main(String[] args){
Integer i = 10;
int j = 10;
System.out.print("i+j = "+(i+j));
}
打印结果为:
Paste_Image.png反编译class文件得到:
Paste_Image.png第一行是装箱过程,查看源码实际是调用了Integer.valueOf(int)方法返回一个Integer实例
第三行进行加法运算的时候是拆箱过程,将Integer实例i拆箱为int型,然后进行加法运算,实际是调用了Integer.intValue()方法返回一个int型数据。
关于装箱与拆箱大致的都讲的差不多了。下面举几个面试中关于装箱与拆箱会问到的例子:
public class Main
{
public static void main(String[] args)
{
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i1==i2); //返回true
System.out.println(i3==i4); //返回false
}
}
下面来解释下这个原因
//这是装箱的源码
public static Integer valueOf(int i) {
return i >= 128 || i < -128 ? new Integer(i) : SMALL_VALUES[i + 128];}
/** * A cache of instances used by {@link Integer#valueOf(int)} and auto-boxing */
private static final Integer[] SMALL_VALUES = new Integer[256];
static {
for (int i = -128; i < 128; i++){
SMALL_VALUES[i + 128] = new Integer(i);
}
}
//当值在[-128,127)之间时,装箱操直接取已经cache好的Integer实例,所以i1==i2,当大于127时,每次都是new出来的Integer,指向不同的对象,所以i3!=i4
public class Main {
public static void main(String[] args) {
Integer a = 1;
Integer b = 2;
Integer c = 3;
Integer d = 3;
Integer e = 321;
Integer f = 321;
Long g = 3L;
Long h = 2L;
System.out.println(c==d); //true
System.out.println(e==f); // false
System.out.println(c==(a+b)); // true
System.out.println(c.equals(a+b));//true
System.out.println(g==(a+b));//false
System.out.println(g.equals(a+b));//false
System.out.println(g.equals(a+h));//true
}
}
// Example 1: == comparison pure primitive – no autoboxing
int i1 = 1;
int i2 = 1;
System.out.println("i1==i2 : " + (i1 == i2)); // true
// Example 2: equality operator mixing object and primitive
Integer num1 = 1; // autoboxing
int num2 = 1;
System.out.println("num1 == num2 : " + (num1 == num2)); // true
// Example 3: special case - arises due to autoboxing in Java
Integer obj1 = 1; // autoboxing will call Integer.valueOf()
Integer obj2 = 1; // same call to Integer.valueOf() will return same
// cached Object
System.out.println("obj1 == obj2 : " + (obj1 == obj2)); // true
int obj3 = 3;
Integer obj4 = 3;
Integer obj5 = new Integer(3);
System.out.println("obj4 == obj5 : " + (obj4 == obj5));//false
System.out.println("obj3 == obj5 : " + (obj3 == obj5));//true
// Example 4: equality operator - pure object comparison
Integer one = new Integer(1); // no autoboxing
Integer anotherOne = new Integer(1);
System.out.println("one == anotherOne : " + (one == anotherOne)); // false
//当 "=="运算符的两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。另外,对于包装器类型,equals方法并不会进行类型转换