关于 Integer 的一个坑

时间:2021-09-06 05:15:17

  其实 JDK 中有很多小坑, 我们稍微不注意, 就掉进去了, 然后调了半天 bug, 也不知道为何. 很闹心!

  这里说一下, 在Integer中的一个小坑.

看一个小例子:

    @Test
    public void testInteger(){
        Integer a = 1;
        Integer b = 1;
        Integer c = 128;
        Integer d = 128;
        System.out.println(a == b); //true
        System.out.println(c == d); //false
    }

  运行可以看到输出结果, 就是注释. 可以看到两个 128 尽然不相等! 闹什么呢? 逗我呢?

别着急, 先来看看执行 Integer a = 1; 之后 Integer 会做什么. 从JDK5 之后就有自动装箱和拆箱了. 给 Integer 赋值一个 int 数字, 当然会发生自动装箱, 封装成一个 Integer 对象.

怎么封装的呢? 在编译的时候, 会调用 Integer 中的  public static Integer valueOf(int i) 方法, 然而在这个方法中, 就存在一个小坑.

看一下方法体:

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

IntegerCache.low: 默认的是 -128

IntegerCache.high: 这是可配置的项(-Djava.lang.Integer.IntegerCache.high=200), 默认的值是 127

在编译阶段, 如果将原始类型 int赋值给Integer, 编译时, 就会自动的调用Integer中的valueOf(int)方法(自动装箱)

也就是说:

  1. 如果Integer的值在 -128与127之间, 那么这个值就会被缓存, 当你取这个值的时候, Integer类从缓存中取出来直接返回, 比较的是数字.

  2. 如果Integer的值不在 -128与127之间, 那么当你取这个值得时候, Integer会为你new一个新对象出来, 这时候比较的是地址, 所以是false

扩展:

  1. Boolean类型的两个值 true和false都会白cache.

  2. Byte的256个值全部cache在内存中.

  3. Short, Long两种类型的cache范围同Integer, 但是不同的是无法调整他们的大小, 代码中完全写死.

  4. Float, Double 没有 cache, 在实际场景中 cache 需要自己做.

  5. 当 Integer 与 int 类型比较时, 这时候会将Integer自动转换为int类型, 也就是通过调用 intValue()方法返回数字, 直接比较数字, 不会出现例子中的问题.

  6. Integer 做 > >= < <= 比较的时候, Integer会自动拆箱, 就是比较他们的数字值.

  7. switch case 为选择语句, 匹配的时候不会用 equals(), 而是直接用 "==", 而在 switch case语句中, 语法层次面 case部分是不能写 Integer 对象的, 只能是普通数字, 如果是普通的数字就会将传入的Integer自动拆箱. 所以不会出现例子中的问题.

@Test
public void testSwitchCase(){
    Integer i = new Integer(3);
    switch (i) {
    case 2:
        System.out.println("1");
        break;
    default:
        break;
    }
}

case 2: 不能写为 case i: