剖析valueOf方法,深入探究Integer缓存实现机制

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

一. 问题展现

我们在面试的时候面试官经常会给面试者洒出一些迷雾用来迷惑面试者这时往往就需要面试者掌握底层源码才能对问题进行较好的回答。接下来壹哥就以Integer的缓存数组为例通过分析其源码来教会大家该如何应对带有迷惑性的面试。

为了讲解清楚壹哥给大家设计了一段代码如下我们可以运行下面这段代码

 public class Test{
     public static void main(String[] args){  
         Integer num1 = 100;
         Integer num2 = 100;
         System.out.println(num1==num2);
         
         Integer num3 = 1000;
         Integer num4 = 1000;
         System.out.println(num3==num4);
    }
 }

上面这段代码中其实就涉及到了关于Integer缓存机制相关的一些面试题比如面试官会问我们”你知道Integer的缓存机制吗“”Integer.valueOf()方法的源码你熟悉吗“”int和Integer的区别有哪些“......

二. 结果分析

上面代码输出的结果应该是

true
false

上面代码中有两个不一样的输出结果本来明明以为是一样的结果其实却不然为什么这两个输出的结果一个是true另一个却是false呢

其实这里num1和num2是在Integer的缓存数组中直接获取的整型缓存对象而num3和num4却都是在直接new出来的Integer对象。至于为什么会这样壹哥会结合Integer的源码对这个问题进行详细说明。

三. 源码解析

    • 反编译结果

表面上看上面的代码中都是把int类型的数值赋给了一个Integer引用类型。但是我们知道一个基本的数据类型赋值给引用类型会进行装箱操作。那么Integer类又是如何将一个基本的int类型转变为引用类型的呢具体过程到底如何呢咱们有必要通过一些反编译工具进行反编译一些上述代码反编译后得到的代码如下

public class TestInt{
    public TestInt(){
    }
      
    public static void main(String args[]){
        Integer num1 = Integer.valueOf(100);
        Integer num2 = Integer.valueOf(100);
        System.out.println(num1 == num2);
             
        Integer num3 = Integer.valueOf(1000);
        Integer num4 = Integer.valueOf(1000);
        System.out.println(num3 == num4);
    }
}
    • valueOf()源码分析

从反编译的结果中我们可以看到反编译后的代码除了添加了一个默认的无参构造外还将原来直接赋值的方式变成了调用Integer的valueOf方法很显然就是在这个方法中进行类型的转换操作的下面我们就打开valueOf这个方法的源码来一探究竟。

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

从源码中可以看到valueOf方法的实现其实比较简单我们很容易就能看出该方法的脉络那就是如果i值在IntegerCache.low和IntegerCache.high范围之间则返回数组中的一个对象如果超过了这个范围就会使用这个数值创建一个新的Integer对象并返回。

    • IntegerCache源码

至于具体的执行情况如何咱们还得打开IntegerCache这个类来查看。

private static class IntegerCache {
        static final int low = -128;//缓存数组最小值设置为-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");
            //判断如果这个值不是空则把这个值和127比较取大值赋给high,同时对数组的范围进行
            //了限制保证数组的长度不能超过int类型的最大值
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);//获取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++);//把-128到high的数据一一赋进缓存数组中
            assert IntegerCache.high >= 127;
        }
        private IntegerCache() {}
}

从上面的源码中我们可以看到IntegerCache这个类是Integer中的一个内部类。这个类里定义了一个Integer常量缓存数组cache这个数组可以缓存-128到high这个变量之间的所有数据。这个high值默认是127但是我们也可以自己设置但必须保证最大值至少要大于等于127同时还要保证数组的长度不会超过整数类型的最大值因为这个数组要缓存所有的-128到high之间的值。上面源码中第23行代码到27行的代码就是将-128到high之间的数通过new Integer()的方式生成Integer类型的对象并放在数组中。

    • valueOf再探究

接下来我们再回到valueOf方法来看一下

可以看出在默认情况下100属于-128到127的范围因此num1和num2都是使用的缓存数组中的同一个对象。而1000在缓存数组缓存的范围之外因此是重新创建了两个Integer对象并分别赋值给num3和num4因此num1和num2比较时地址是相等的而num3和num4的地址是不等的

四. 知识扩展

    • 修改high值

如果我们设置了high值是1000应该看到num3==num4的结果也应该是true。我们可以在下面验证一下这里要通过虚拟机参数设置high值以eclipse为例具体设置方法见下图

    • 再次执行

如上图对high参数设置之后high值变为了1000我们点击run按钮重新运行结果如下图

可以看出结果如咱们所料此时两个结果均为true

五. 总结

经过上面壹哥给大家的分析你现在是不是已经对Integer的缓存机制有了深入的认识呢最后咱们再把上述内容总结一下看看该如何清晰地回答面试官的问题吧对于这类面试题我们可以这么回答

在我们给Integer对象赋值时其实是调用了Integer类的valueOf这个静态方法而这个静态方法使用了Integer类中的IntegerCache内部类。
在IntegerCache这个内部类中存在一个Integer对象的缓存数组这个数组中默认缓存了从-128到127的所有Integer对象。
当我们所赋的值在这个范围之间的时候会直接从数组中获取Integer的缓存对象因此两次100都是同一个对象。但是1000超出了这个范围就会重新创建一个Integer对象因此两次1000不是同一个对象。

至此面试官的问题已经回答清楚了。如果你还记得住该如何设置high的最大值也可以把最大值的设置过程给面试官讲讲这个就属于锦上添花的回答了。

以上就是壹哥对Integer缓存机制面试题的分析过程现在你知道该怎么去回答这个面试题了吗欢迎大家在评论区给壹哥留言说说你的感悟或疑惑吧。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6