• Integer小知识,涉及IntegerCache,1000==1000为false而100==100为true


    具体现象及原理

    这是一个挺有意思的讨论话题。

    如果你运行下面的代码:

    Integer a = 1000, b = 1000;  
    System.out.println(a == b);
    Integer c = 100, d = 100;  
    System.out.println(c == d);
    

    你会得到

    false
    true
    

    基本知识:我们知道,如果两个引用指向同一个对象,用==表示它们是相等的。如果两个引用指向不同的对象,用==表示它们是不相等的,即使它们的内容相同。

    因此,后面一条语句也应该是false 。

    这就是它有趣的地方了。如果你看去看 Integer.java 类,你会发现有一个内部私有类,IntegerCache.java,它缓存了从-128到127之间的所有的整数对象。

        /**
         * 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() {}
        }
    

    所以事情就成了,所有的小整数在内部缓存,然后当我们声明类似下面变量的时候,

    Integer c = 100;
    

    它会进行自动装箱,实际上在内部调用了Integer.valueOf方法:

    Integer i = Integer.valueOf(100);
    

    现在,如果我们去看valueOf()方法,我们可以看到

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

    如果值的范围在-128到127之间,它就从高速缓存返回实例。

    Integer c = 100, d = 100;
    

    指向了同一个对象。

    这就是为什么我们写

    System.out.println(c == d);
    

    我们可以得到true。

    现在你可能会问,为什么这里需要缓存?

    合乎逻辑的理由是,在此范围内的“小”整数使用率比大整数要高,因此,使用相同的底层对象是有价值的,可以减少潜在的内存占用。

    借助反射小实验

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Class[] caches = Integer.class.getDeclaredClasses();
        Class cache = Integer.class.getDeclaredClasses()[0]; 
        Field myCache = cache.getDeclaredField("cache"); 
        myCache.setAccessible(true);
        Integer[] newCache = (Integer[]) myCache.get(cache); 
        newCache[132] = newCache[133]; 
        int a = 2;
        int b = a + a;
        System.out.printf("%d + %d = %d", a, a, b);
    }
    

    输出的是

    2 + 2 = 5
    

    详细解释

    //获取Integer.class下所有的类,Integer.class只有一个内部类就是IntegerCache.class
    Class[] caches = Integer.class.getDeclaredClasses();
    //取出第一个类,也就是IntegerCache.class
    Class cache = Integer.class.getDeclaredClasses()[0]; 
    //获取IntegerCache类下的cache字段
    Field myCache = cache.getDeclaredField("cache"); 
    //设置可以访问,可以修改值
    myCache.setAccessible(true);
    //获取cache字段的具体值
    Integer[] newCache = (Integer[]) myCache.get(cache);
    //将数组的第132个数值更改为第133个数值的值。第132个值原本为4,第133个值为5
    newCache[132] = newCache[133]; 
    //从IntegerCache类中cache数组取出值来。IntegerCache.cache[2 + (-IntegerCache.low)],IntegerCache.low值为-128,此时取的是下标130处的值,为2
    int a = 2;
    //a+a的值为4,IntegerCache.cache[4 + (-IntegerCache.low)],IntegerCache.low值为-128,此时取的是下标132处的值,已经修改为5,此时b的值已经是5
    int b = a + a;
    //此时就会出现,2 + 2 = 5 的情况,归根结底还是IntegerCache中cache数组缓存的原因,-128~127之间的数值都从缓存中取值
    System.out.printf("%d + %d = %d", a, a, b);
    

    站在巨人肩膀上摘苹果

    https://mp.weixin.qq.com/s/mBs5k2LnoGMerpE2vAQzyg

  • 相关阅读:
    原生JS发送Ajax请求、JSONP
    python Flask中html模版中如何引用css,js等资源
    python 单例与数据库连接池 及相关选择
    费曼致学生真野光一
    Winform判断EventHandler是否已经添加
    The list of pre-build cross-compiler
    Extended VM Disk In VirtualBox or VMware (虚拟机磁盘扩容)
    一张图了解SSH端口转发
    显卡驱动卸载清理工具
    Oracle分析函数
  • 原文地址:https://www.cnblogs.com/eternityz/p/13640390.html
Copyright © 2020-2023  润新知