底层源码面试题丨深入剖析Integer缓存机制相关的问题!

一. 问题展现

我们在面试的时候,面试官往往会给面试者洒出一些迷雾,用来迷惑面试者,这时往往就需要面试者掌握底层源码,才能对问题进行较好的回答。接下来波哥就以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的源码对这个问题进行详细说明。

三. 源码解析

1. 反编译结果

表面上看,上面的代码中都是把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);
    }
}

2. 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对象并返回。

3. 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类型的对象,并放在数组中。

4. valueOf再探究

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

底层源码面试题丨深入剖析Integer缓存机制相关的问题!_第1张图片

可以看出,在默认情况下,100属于-128到127的范围,因此num1和num2都是使用的缓存数组中的同一个对象。

而1000在缓存数组缓存的范围之外,因此是重新创建了两个Integer对象,并分别赋值给num3和num4,因此num1和num2比较时地址是相等的,而num3和num4的地址是不等的!

四. 知识扩展

1. 修改high值

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

底层源码面试题丨深入剖析Integer缓存机制相关的问题!_第2张图片

2. 再次执行

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

底层源码面试题丨深入剖析Integer缓存机制相关的问题!_第3张图片

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

五. 总结

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

  • 在我们给Integer对象赋值时,其实是调用了Integer类的valueOf这个静态方法,而这个静态方法使用了Integer类中的IntegerCache内部类。

  • 在IntegerCache这个内部类中存在一个Integer对象的缓存数组,这个数组中默认缓存了从-128到127的所有Integer对象。

  • 当我们所赋的值在这个范围之间的时候,会直接从数组中获取Integer的缓存对象,因此两次100都是同一个对象。但是1000超出了这个范围,就会重新创建一个Integer对象,因此两次1000不是同一个对象。

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

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


*威哥Java学习交流Q群:691533824
加群备注:CSDN推荐

你可能感兴趣的:(分布式,缓存,java,面试)