知识归纳笔记(二):考查自动装箱与拆箱
自动装箱与拆箱
最近遇到一个面试题,是关于自动装箱和拆箱相关的,代码如下所示
public static void main(String[] args) { Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150; System.out.println(f1 == f2); //true System.out.println(f3 == f4); //false }
老规矩反编译一下,代码如下:
public class Test { public static void main(String[] args) { Integer f1 = Integer.valueOf(100); Integer f2 = Integer.valueOf(100); Integer f3 = Integer.valueOf(150); Integer f4 = Integer.valueOf(150); System.out.println(f1 == f2); System.out.println(f3 == f4); }
答题准备:知识点复习
- 自动装箱
- 定义:装箱就是自动将基本数据类型转换为包装器类型。
- 实质:调用Integer.valueOf()
- 自动拆箱
- 定义:自动将包装器类型转换为基本数据类型
- 实质:调用Integer.intValue()
自动装箱拆箱过程如下代码:
public static void main(String[] args) { //自动装箱 Integer boxing = 99; //自定拆箱 int unpacking = boxing; }
反编译后代码如下:
public static void main(String[] args) { Integer boxing = Integer.valueOf(99); int unpacking = boxing.intValue(); }
解题前言:装箱与拆箱类型有很多,我们在这里只以Integer类型举例
- 基本类型:byte、short、int、long、float、double、char、boolean
- 基本引用类型:Byte、Short、Integer、Long、Float、Double、Character、Boolean
(1). 看到f1 == f2 我们第一想到的是 f1与f2都是引用类型,比较的是地址,所以得出结论是false。然而看到结果f1 ==f2 是true,就一大堆问号飘过。废话不多说,我们追本溯源研究一下源代码吧,点进Integer.valueOf(100)方法,我们看到如下代码:
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
它会首先判断i的大小:如果 i 大于等于IntegerCache.low与 i 小于等于IntegerCache.high时调用IntegerCache.cache[i + (-IntegerCache.low),否则new Integer(i)对象。要想知道 i 的范围,我们又得研究一下IntegerCache,通过IDEA工具点击进入发现IntegerCache是Integer的内部类,其代码如下所示:
/** * Cache to support the object identity semantics of autoboxing for values between * -128 and 127 (inclusive) as required by JLS. * * The cache is initialized on first usage. The size of the cache * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option. * During VM initialization, java.lang.Integer.IntegerCache.high property * may be set and saved in the private system properties in the * sun.misc.VM class. */ 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() {} }
查看内部类上面的注释,大致意思是 缓存支持JLS要求的-128和127(含)。缓存在首次使用时会初始化(在常量池中)。缓存的大小可以由{@code-xx:autoboxcachemax=<size>}选项控制。在虚拟机初始化期间,java.lang.Integer.IntegerCache.High属性可以设置并保存在sun.misc.vm类(配置类)。
看到上述解释以及代码,我们得出结论:当传入某个值大于等于IntegerCache.low(-128)且小于等于IntegerCache.high(默认127)时调用IntegerCache.cache[i + (-IntegerCache.low)(初始化时缓存的[-128, 127] 值),否则new Integer(i)对象。然后回过来看看f1和f2,其值[-128, 127]范围内,并没有重新new一个对象,这就不难得出f1 == f2是 true了。
(2).同理(一), 我得出150并不在[-128, 127]范围内,所以f3和f4都new一个新对象放在堆中,这就不难得出f3 == f4是 false了。
总结
其他封装类类型初始化值范围
类型 | 初始化值范围 |
Integer | [-128,127] |
Byte | [-128,127] |
Boolean | TRUE,FALSE |
Short | [-128,127] |
Long | [-128,127] |
Float | 无 |
Double | 无 |
Character | (~,127] |
Integer的JVM简易图
题内以及题外知识点归纳
- 引发装箱和拆箱
- 当两种不同类型用==比较时,包装器类的需要拆箱, 当同种类型用==比较时,会自动拆箱或者装箱
- equals(Object o) 因为原equals方法中的参数类型是封装类型,所传入的参数类型(a)是原始数据类型,所以会自动对其装箱,反之,会对其进行拆箱
- 封装类型在首次使用时会将某个范围值缓存初始化(在常量池中),在装箱过程中,如果不在其范围内会重新new一个全新的对象,否则取常量池中的对象
- 装箱操作会创建对象,频繁的装箱操作会消耗许多内存,影响性能,所以可以避免装箱的时候应该尽量避免。