从Integer切入装箱与拆箱

jdk版本问题

在jdk1.5之前即javaSE1.5之前,是不存在自动装箱和自动装箱概念的。

装箱

jdk1.5之前创建Integer对象需要如下代码

Integer i1 = new Integer(1);

在jdk1.5之后,有了自动装箱和自动拆箱的概念后,创建Integer对象只需如下代码

Integer i1 = 1;

这个过程被称为自动装箱。

拆箱

在jdk1.5之前,若想把Integer类型的值赋值给基本数据类型int,需要用到intValue()方法,代码如下:

public class IntegerDemo {
	public static void main(String[] args) {
		Integer i1 = 1;
		int a = i1.intValue();
	}
}

在jdk1.5之后,在有了自动拆箱的概念之后,代码便可如下形式

public class IntegerDemo {
	public static void main(String[] args) {
		Integer i1 = 1;
		int a = i1;
	}
}

这里将Integer的值直接赋值给基本数据类型i被称为自动拆箱。

如何实现自动装箱和自动拆箱

这里我们可以使用java的反编译指令查看自动装箱和自动拆箱的过程

public class IntegerDemo {
	public static void main(String[] args) {
		Integer i1 = 1;
		int i = i1;
	}
}

反编译结果
在这里插入图片描述
我们给Integer对象直接赋值的时候,JVM虚拟机会帮我们自动调用Integer.valueOf方法,Integer.valueOf方法返回值为Integer类型,这就是所谓的自动装箱的原理。

我们将Integer对象i1的值直接赋值给基本数据类型i的时候,JVM虚拟机会帮我们调用Integer.intValue方法,Integer.intValue方法的返回值类型为int,这就是自动拆箱的过程。

其他的包装类,例如Double、Long等等都存在自动装箱和自动拆箱,原理与Integer相同!

面试中关于装箱和拆箱的问题

public class IntegerDemo {
	public static void main(String[] args) {
		Integer i1 = 50;
		Integer i2 = 50;
		Integer i3 = 200;
		Integer i4 = 200;
		Integer i5 = new Integer(50);
		Integer i6 = new Integer(50);
		System.out.println(i1 == i2); // true
		System.out.println(i3 == i4); // false
		System.out.println(i3.equals(i4)); // true
		System.out.println(i5 == i6); // false
		System.out.println(i1 == i5); // false
		System.out.println(i1.equals(i5)); // true
	}
}

解析
这里为什么i1==i2得到true而i3==i4却得到false
这里涉及到了Integer源码中的IntegerCache内部类,下面解析下IntegerCache类

private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            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);
                    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() {}
    }

从代码不难看出,IntegerCache类中存在一个Integer cache[],这是因为jdk开发人员考虑到integer的经常使用,把-128~127共256个数值存放于256个integer对象中,然后把这些integer对象存放在了Integer cache[]数组中,在编写代码时,若直接给integer对象直接赋值,会先去Integer cache[]数组中查询,若该值在Integer cache[]数组中,直接使用Integer cache[]数组中的integer对象,不需再新new出integer对象。
所以也不难得出为何i1==i2为true而i3==i4为false。
而i5和i6都是new出来的integer对象,所以i5==i6肯定为false。
而integer类中重写了equals方法,所以在执行equals方法时,比较的是数值大小,所以i3.equals(i4)和i1.equals(i5)结果都为true。

另外一种情况

public class IntegerDemo {
	public static void main(String[] args) {
		Integer i1 = 1;
		Integer i2 = 2;
		Long l1 = 1L;
		Long l2 = 2L;
		Long l3 = 3L;
		System.out.println(l3 == (i1 + i2)); // true
		System.out.println(l3.equals(i1 + i2)); // false
	}
}

这里需要解释一下包装类对象在进行==运算时需要注意的地方
如果==号左右两边均为包装类对象的引用,则进行比较的是两边包装类对象的引用是否指向的是同一个对象,即它们指向的“地址”是否相同(这里说明一下,java中没有地址的概念,这里只是便于理解)。
如果等号两边出现了算数表达式(即出现了算数运算符),则两边都会触发自动拆箱,比较的不再是是否指向的是不是同一个对象,而是比较的数值大小。
那么上述代码中第一行得出true也不难理解。

包装类中的equals方法这里以Integer和Long为例

Integer的equals方法

    public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

Long的equals方法

    public boolean equals(Object obj) {
        if (obj instanceof Long) {
            return value == ((Long)obj).longValue();
        }
        return false;
    }

这里不难看出,当包装类对象使用equals方法时,equals不会进行类型转换,从而得出上述代码第二行应输出false。

浮点数的包装类

浮点数包装类中不存在cache数组,因为浮点数在任意大小区间都有无数多个数,下面代码可以证明。

public class IntegerDemo {
	public static void main(String[] args) {
		Double d1 = 1.0;
		Double d2 = 1.0;
		System.out.println(d1 == d2);   //false 
	}
}

这里输出false可以证明在浮点数(Double和Float)中不存在cache数组概念。

Boolean包装类

直接看代码

public class IntegerDemo {
	public static void main(String[] args) {
		Boolean b1 = false;
		Boolean b2 = false;
		Boolean b3 = true;
		Boolean b4 = true;
		System.out.println(b1 == b2); // true
		System.out.println(b3 == b4); // true
		System.out.println(b1 == b3); // false
	}
}

这里需要说明一下
在Boolean类的源码中存在两个final定义的常量

 public static final Boolean TRUE = new Boolean(true);
 public static final Boolean FALSE = new Boolean(false);

我们再来看一下Boolean的valueOf方法

    public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }

我们可以理解为Boolean包装类中也存在有cache数组,只不过数组中只有两个对象,TRUE和FALSE,每当Boolean进行自动装箱时,总是从这两个对象中取,所以也不难理解上述代码中得到的结果。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值