在最近准备安卓面试的过程中,一位资深的大神问了我关于装箱和拆箱的一些知识点。无奈小弟平时没怎么关注这个方面的知识点,拿来就是用,完全没有没有考虑到使用上的性能损耗和一些注意事项问题。所以经历此面试之后,决定好好复习一下关于这方面的知识点,并总结出这篇文章,供自己日后快速复习,同时也希望本篇文章能给各位看官带来收益。
1 自动装箱 & 自动拆箱
2 比较符“==”和“equal”,在使用上的注意事项
public static void main(String[] args) { Integer a = 1; Integer b = 2; Integer c = 3; Integer d = 3; Integer e = 220; Integer f = 220; Long g = 3L; System.out.println(c == d); System.out.println(e == f); System.out.println(c == (a + b)); System.out.println(c.equals(a + b)); System.out.println(g == (a + b)); System.out.println(g.equals(a + b)); } 复制代码
小伙伴们先猜想上面程序输出的结果,认真的思考一下。
在程序中变量:a,b,c,d,e,f,g都是对基本数据包装类,在初始化这些变量的时候,实际上java编译器帮我们使用装箱来赋值。例如:Integer a = 1; 编译后换算成 Integer a = Integer.valueOf(1); 这个过程就被称为装箱啦,需要注意一点的是变量a为Integer对象类型,为实际保存值的地址引用。
当打印:c == d的时候,此时两者的对象类型都是Integer,那么此时比较的是两者的地址引用。此处的结果返回为 true ,那么我们能间接得出结论: c和d都指向同一个对象,指向的对象值为3
当打印:e == f的时候,同样的此时两者的对象类型都是Integer,那么此时比较的是两者的地址引用。但是此处返回的结果为 false ,那么我们能间接得出结论: e和f指向不同的对象,但是对象的值都为220 。 对比上面两条结果,我们是否有疑问。为啥第一条打印为true,第二条打印为false。想要搞懂这其中的原因,那么就需要知道Integer中的Cache的概念,下面贴出其中的源码:
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) { // If the property cannot be parsed into an int, ignore it. } } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; } private IntegerCache() {} } 复制代码
上面的源码不是特别复杂,各位看官可以先大致观摩一下,再来看我的解析,理解起来能够事半功倍。 源码解析:IntegerCache是Integer的静态内部类,用来缓存Integer对象。 使用数组的形式来缓存,并且数组的大小为256,缓存值的范围为:[-128 ,127]。如果值的取值范围没有在这个区间中,那么这个对象将不会保存
结论:值为3的Integer对象,会保存在缓存中。值为220,不在保存的区间范围内,所以每次都是创建新的Integer类型对象
当打印“c == (a+b)” 的时候,结果为true。为什么为true?
我来解析一下这个过程: a+b 等同于a.value+b.value , 其中a.value = 1,b.value = 2 ,所以:a+b的结果为基本类型3(这个过程就是拆箱的过程,一般在遇到算数运算符的时候,包装类型就是自动拆箱)“c == (a+B)” 就等同于 “c == 3”,因为此时的c的类型为Integer,所以需要拆箱比较,最终的比较形式为:c.value == 3
当打印“c.equals(a+b)”的时候,结果也为true 和上面一样,解析一下过程: a+b 的解析和上面一样。我们重点来看"c.equals(a+b)",Integer.equals()方法需要传入Object对象,那么a+b需要装箱变为Integer对象。程序就变成“c.equals(Integer.value(3))”,所以结果为true
注意事项:包装类的装箱和拆箱涉及到性能损耗,因为程序需要多执行几步。涉及到基本算术运算符,就会涉及到拆箱,变量初始化涉及到装箱
当打印“g == (a + b)”,因为(a+b)得到结果为基本类型3。变量g的类型为Long,当执行g == 3的时候需要拆箱变成3 == 3 。所以得到的结果为true。
当打印“g.equals(a + b),结果为false。为什么结果不是true,因为:g的类型为Long,那么Long.equals()方法比较的对象类型也需要为Long,源码如下:
public boolean equals(Object obj) { if (obj instanceof Long) { return value == ((Long)obj).longValue(); } return false; } 复制代码
因为a+b得到的结果为整型3,所以程序变成g.equals(Integer.valueOf(3)),可以看到两者对象的类型都不相同,尽管他们的值都为3,但程序结果还是返回false