Java中128陷阱源码剖析(一看就懂)

时间:2024-03-26 09:15:49

文章目录

  • 128陷阱
    • 例子
    • 解析
      • 问题一
      • 问题二
      • 问题三

128陷阱

例子

现在有这样的程序,相同的值判断相等,不同的值大小下,得出的结果却不同。

public class Main {
    public static void main(String[] args) {
        Integer a= 123;
        Integer b = 123;
        int c = 123;
        int d = 123;

        System.out.println(a == b); // T
        System.out.println(c == d); // T
        System.out.println(a == c); // T
    }
}
public class Main {
    public static void main(String[] args) {
        Integer a= 128;
        Integer b = 128;
        int c = 128;
        int d = 128;

        System.out.println(a == b); // F
        System.out.println(c == d); // T
        System.out.println(a == c); // T
    }
}

问题一:a,b为什么不同值下判断相等的结果不同?
问题二:c,d为什么相同?
问题三:a,c为什么一直相同?

解析

问题一

这就是Integer的128陷阱。

在Integer创建时,其实就是走的Integer.valueOf()返回了一个对象,例如:

Integer a = 100;
相当于
Integer a = Integer.valueOf(100);

我们看ValueOf这个方法

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

先翻译一下注释:

Returns an Integer instance representing the specified int value. If a new Integer instance is not required, this method should generally be used in preference to the constructor Integer(int), as this method is likely to yield significantly better space and time performance by caching frequently requested values. This method will always cache values in the range -128 to 127, inclusive, and may cache other values outside of this range.
返回一个 Integer 实例,该实例表示指定的 int 值。如果不需要新的 Integer 实例,则通常应优先使用此方法,而不是构造函数 Integer(int),因为此方法可能会通过缓存频繁请求的值来产生显著更好的空间和时间性能。此方法将始终缓存 -128 到 127(含)范围内的值,并可能缓存此范围之外的其他值。

我们再看代码:
它其实是判断了一下数是否在IntegerCache.low和IntegerCache.high之间。

所以我们再看IntegerCache这个静态内部类,静态内部类在被调用时加载。

他是创建了一个Integer的cache数组,大小为127-(-128)+ 1,并且都new了出来(对应初值也赋值了),所以映射关系就很明显了,就是用来存放[-128, 127]之间的数,上面调用的low和high就是这两个数。

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

所以如果我们赋值的数i在[-128, 127]之间,他就会返回cache[i - (-128)]中已经创建好的对应Integer对象。
如果不在之间,就会返回一个新new的Integer对象。

也就是说在[-128, 127]之间,是同一个对象,而不在范围内,则是不同的对象。

回到我们最初的问题,==对应引用类型,其实对比的是否为同一引用,范围内,同一对象返回true,不在范围内,是不同对象返回false。

问题二

这个就不用解释了吧,基本类型比较值,肯定相同。

问题三

而对于Integer 和 int 相同值进行比较为什么一直相等的原理,是这样的:

public class Main {
    public static void main(String[] args) {
        Integer a= 128;
        int c = 128;
        System.out.println(a == c); // T
    }
}

在==时候,jvm其实调用的是

a.intValue() == c

那我们来看一下intValue这个方法的代码:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这个value是什么呢?

在这里插入图片描述

就是Integer的构造方法,给value赋的int值,对应我们真实的int,无论你是否是在cache缓存内,Integer对象都是new出来的,这个value就是真实的记录的int值。

那这个==不就变成了问题二了么,就是值的对比,很显然无论什么情况都是true。

over!