信息发布→ 登录 注册 退出

如何通过源码了解Java的自动装箱拆箱详解

发布时间:2026-01-11

点击量:
目录
  • 什么叫装箱 & 拆箱?
  • 首先看一段代码
  • 装箱(valueOf())
    • 为什么要有[-128,127]的缓存?
    • 为什么是[-128,127]?
    • 自动装箱带来的性能问题
    • 小总结
  • 拆箱(intValue)
    • 补充:自动装箱、拆箱总是会发生吗?
      • 总结

        什么叫装箱 & 拆箱?

        将int基本类型转换为Integer包装类型的过程叫做装箱,反之叫拆箱。

        首先看一段代码

        public static void main(String[] args) {
            Integer a = 127, b = 127;
            Integer c = 128, d= 128;
            System.out.println(a == b); // true
            System.out.println(c == d); // false
        }
        

        不知道还有没有人不知道这段代码出现true和false的原因。由此我们引出了Java装箱的这个操作。我们带着疑问去进行分析。

        装箱(valueOf())

        public static Integer valueOf(int i) {
            // -128 - 127
            if (i >= IntegerCache.low && i <= IntegerCache.high)
                return IntegerCache.cache[i + (-IntegerCache.low)];
            return new Integer(i);
        }
        

        我们可以发现,在最开始有一个判断,如果这个值的范围在[-128,127]之间,那么就从这个缓存(Integer数组)中取,如果不在这个范围那么直接new一个。

        为什么要有[-128,127]的缓存?

        我说说的理解,因为在我们的业务中,可能存在各种状态和标识等Integer类型的字段,这些值一般都是0,1,2,3之类的,而且出现的比较频繁,如果没有缓存,那么就需要频繁的new对象,然后再释放,就非常消耗内存空间,所以对于这个缓存就出现了,可以极大的帮助我们优化一些空间上的浪费。

        为什么是[-128,127]?

        这个我看了一下,具体为什么这里就不详说了,主要还是依赖计算机基础知识,在你了解了什么是原码、反码、补码。就很容易知道为什么是这个范围区间了。

        这个值也是可以通过启动参数进行更改的。

        -XX:AutoBoxCacheMax=(size)

        自动装箱带来的性能问题

        那么看到现在你应该明白上面代码出现不同结果的原因了,那么你有没有想过,比如我们业务中一个for循环中,出现了统计数据类似这样的操作,如果存在自动装箱,那么会出现什么问题?我们看下面一段代码。

        public static void main(String[] args) {
            long startTime = System.currentTimeMillis();
            Integer count = 0;
            // int count = 0;
            for (int i = 0; i < 5000000; i++) {
                count += i;
            }
            System.out.println("计算时长:" + (System.currentTimeMillis() - startTime) + " ms");
        }
        
        // 执行结果:
        // Integer 计算时长:51 ms
        // int 计算时长:6 ms

        那么通过执行结果可以明显的发现自动装箱频繁的new对象、分配内存,造成时间和空间上的性能损耗。

        小总结

        通过上面的源码阅读和测试分析,我们可以得出结论,我们平时在进行计算统计,或者方法入参的时候,应该尽量的避免这种类型转换的问题。来提升我们整个代码的执行效率。

        拆箱(intValue)

        拆箱总体没有什么复杂的逻辑,直接返回这个数值的基本类型。

        补充:自动装箱、拆箱总是会发生吗?

        其实不一定。看下面的一段示例代码,输出结果已被注释在输出语句后面。

        public static void main(String[] args) {
        // TODO 自动生成的方法存根
        Integer a = 1;
        Integer b = 2;
        Integer c = 3;
        Integer d = 3;
        Integer e = 321;
        Integer f = 321;
        Long g = 3L;
        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));//true
        //equals方法不会处理数据转型关系
        System.out.println(g.equals(a+b));//false
        }

        发生自动装箱、拆箱的情况如下:

        自动装箱:基本类型赋值给包装类型。如:Integer i1 = 1;

        自动拆箱:

        1. 包装类型赋值给基本类型。如:int i2 = new Integer(1);
        2. int类型与Integer类型比较。int类型与Integer类型比较如果值相等则结果总是为true。
        3. Integer类型遇到算术运算

        但是为什么在上例中,System.out.println(c==d);与System.out.println(e==f);输出的结果不一样呢?

        主要是因为Integer.valueOf()方法。Integer的部分源码贴在下面:

          //
           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() {}
            }
            
          public static Integer valueOf(int i) {
                if (i >= IntegerCache.low && i <= IntegerCache.high)
                    return IntegerCache.cache[i + (-IntegerCache.low)];
                return new Integer(i);
            }

        IntegerCache 是Integer的静态内部类,valueOf()是包装方法。从源码中可以看出,cache是一个缓存数组,当valueOf()方法的入参i在[-128,127]区间内,就会返回缓存数组中的Integer值,否则会重新new一个Integer。

        这就是System.out.println(c==d);与System.out.println(e==f);输出结果不同的原因。c和d在缓存区间内,所以返回的是同一个引用;而e和f不在缓存区间内,返回的都是new Integer,已经不是同一个引用。

        总结

        在线客服
        服务热线

        服务热线

        4008888355

        微信咨询
        二维码
        返回顶部
        ×二维码

        截屏,微信识别二维码

        打开微信

        微信号已复制,请打开微信添加咨询详情!