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


    一. 问题展现

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

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

    1.  public class Test{
    2.      public static void main(String[] args){  
    3.          Integer num1 = 100;
    4.          Integer num2 = 100;
    5.          System.out.println(num1==num2);
    6.          
    7.          Integer num3 = 1000;
    8.          Integer num4 = 1000;
    9.          System.out.println(num3==num4);
    10.     }
    11.  }

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

    二. 结果分析

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

    true

    false

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

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

    三. 源码解析

    1. 反编译结果

    表面上看,上面的代码中都是把int类型的数值赋给了一个Integer引用类型。但是我们知道,一个基本的数据类型赋值给引用类型会进行装箱操作。

    那么Integer类又是如何将一个基本的int类型转变为引用类型的呢?具体过程到底如何呢?咱们有必要通过一些反编译工具进行反编译一些,上述代码反编译后得到的代码如下:

    1. public class TestInt{
    2.     public TestInt(){
    3.     }
    4.       
    5.     public static void main(String args[]){
    6.         Integer num1 = Integer.valueOf(100);
    7.         Integer num2 = Integer.valueOf(100);
    8.         System.out.println(num1 == num2);
    9.              
    10.         Integer num3 = Integer.valueOf(1000);
    11.         Integer num4 = Integer.valueOf(1000);
    12.         System.out.println(num3 == num4);
    13.     }
    14. }

    2. valueOf()源码分析

    从反编译的结果中我们可以看到,反编译后的代码除了添加了一个默认的无参构造外,还将原来直接赋值的方式变成了调用Integer的valueOf方法!

    很显然,就是在这个方法中进行类型的转换操作的!下面我们就打开valueOf这个方法的源码来一探究竟。

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

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

    3. IntegerCache源码

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

    1. private static class IntegerCache {
    2.         static final int low = -128;//缓存数组最小值设置为-128
    3.         static final int high;
    4.         static final Integer cache[];
    5.         static {
    6.             // high value may be configured by property
    7.             int h = 127;
    8.             //获取虚拟机参数中设置的上限值
    9.             String integerCacheHighPropValue =
    10.                 sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
    11.             //判断如果这个值不是空,则把这个值和127比较取大值赋给high,同时对数组的范围进行
    12.             //了限制保证数组的长度不能超过int类型的最大值
    13.             if (integerCacheHighPropValue != null) {
    14.                 try {
    15.                     int i = parseInt(integerCacheHighPropValue);
    16.                     i = Math.max(i, 127);//获取127和设置的较大值
    17.                     // Maximum array size is Integer.MAX_VALUE
    18.                     h = Math.min(i, Integer.MAX_VALUE - (-low) -1);//判断是否超过整形最大值
    19.                 } catch( NumberFormatException nfe) {
    20.                     // If the property cannot be parsed into an int, ignore it.
    21.                 }
    22.             }
    23.             high = h;
    24.             cache = new Integer[(high - low) + 1];
    25.             int j = low;
    26.             for(int k = 0; k < cache.length; k++)
    27.                 cache[k] = new Integer(j++);//把-128到high的数据一一赋进缓存数组中
    28.             assert IntegerCache.high >= 127;
    29.         }
    30.         private IntegerCache() {}
    31. }

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

    4. valueOf再探究

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

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

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

    四. 知识扩展

    1. 修改high值

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

    2. 再次执行

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

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

    五. 总结

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

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

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

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

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

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


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

  • 相关阅读:
    启山智软/JAVA商城
    Flutter:安装依赖报错doesn‘t support null safety
    SSM+医保业财一体化管理系统 毕业设计-附源码151023
    数组模拟环形队列(思路分析) [数据结构与算法][Java]
    7个高级程序员才会关注的数据类网站
    Python国庆祝福
    【零基础入门JavaSE】数据类型与变量
    对称加密 vs 非对称加密
    Python顺序表
    ConcurrentHashMap和HashMap的区别
  • 原文地址:https://blog.csdn.net/finally_vince/article/details/125912113