Integer类型比较大小【详解】

面试中经常被问的一个问题就是Integer类型的对象的比较问题,今天我们就来一探究竟

有如下代码:问输出是什么?

public static void main(String[] args) {
        Integer a = 128;
        Integer b = 128;

        Integer c = 1;
        Integer d = 1;

        System.out.println(a==b);
        System.out.println(a.equals(b));
        System.out.println(c==d);
        System.out.println(c.equals(d))
}
//输出:
false
true
true
true

1、equals()

首先我们看比较容易理解的a.equals(b)c.equals(d),我们看下源码,主要涉及代码如下:

private final int value;
public boolean equals(Object obj) {
       if (obj instanceof Integer) {
           return value == ((Integer)obj).intValue();
       }
       return false;
   }
   public int intValue() {
       return value;
   }

可以看到Integer中重写了equals方法,其中就是对于int类型值的比较,所以自然返回true

 System.out.println(a.equals(b));//等价于128==128
 System.out.println(c.equals(d));//等价于1==1

2、==

下面的==比较就比较麻烦了,其实也不麻烦,只是涉及到自动拆箱和Integer对象缓存,
我们先来看一下字节码:javap -v 反编译一下,找到main方法对用的字节码如下:

public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=5, args_size=1
         0: sipush        128
         3: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         6: astore_1
         7: sipush        128
        10: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        13: astore_2
        14: iconst_1
        15: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        18: astore_3
        19: iconst_1
        20: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        23: astore        4
        25: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        28: aload_1
        29: aload_2
        30: if_acmpne     37
        33: iconst_1
        34: goto          38
        37: iconst_0
        38: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
        41: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        44: aload_1
        45: aload_2
        46: invokevirtual #5                  // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
        49: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
        52: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        55: aload_3
        56: aload         4
        58: if_acmpne     65
        61: iconst_1
        62: goto          66
        65: iconst_0
        66: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
        69: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        72: aload_3
        73: aload         4
        75: invokevirtual #5                  // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
        78: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
        81: return

通过字节码可以看到,从0-23是在初始化了4个变量,同时存储之前自动调用了java/lang/Integer.valueOf:(I)Ljava/lang/Integer;进行装箱,25-38对应代码System.out.println(a==b);

 25: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
 28: aload_1							//加载128
 29: aload_2							//加载128
 30: if_acmpne     37					//比较两个Integer引用是否是!=,成立执行33行,将1写入操作数栈,不成立跳转到37,将0写入操作数栈
 33: iconst_1
 34: goto          38
 37: iconst_0
 38: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V//打印30行执行后的结果0或1对应false或true

问题就出现这里,通过输出的结果我们可以看到a和b引用的是不同的Integer,这个可以理解,因为我们就是定义了两个Integer;但是c和d引用的却是同一个Integer,那为什么会这样呢?

public static void main(String[] args) {
        Integer a = 128;
        Integer b = 128;
        Integer c = 1;
        Integer d = 1;
        System.out.println(a==b);
        System.out.println(a.equals(b));
        System.out.println(c==d);
        System.out.println(c.equals(d))
}
//输出:
false
true
true
true

玄机应该是在创建对象的时候,编译器悄悄的干了点什么,干了什么呢?其实就是自动装箱,我们找到自动装箱执行的方法Integer.valueOf的源代码如下:

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

可以看到,当i[IntegerCache.low, IntegerCache.high]中时,没有new新的Integer对象,所以我们在找到IntegerCache一探究竟,源代码如下:

/**
  * 缓存以支持自动装箱的对象标识语义
  * JLS 要求的 -128 和 127(含)。
  *
  * 缓存在第一次使用时初始化。 缓存大小
  * 可以由 {@code -XX:AutoBoxCacheMax=<size>} 选项控制。
  * 在 VM 初始化期间,java.lang.Integer.IntegerCache.high 属性
  * 可以在私有系统属性中设置和保存
  * sun.misc.VM 类。
  */
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) {//如果自己配置了就用自己配置的,同时下面代码保证了,自己配置的值要大于127才生效
                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;//没有自己配置就用默认128

			//创建缓存数组
            cache = new Integer[(high - low) + 1];
            int j = low;
            //缓存数组中全部填充值Integer对象
            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;//最大值必须大于127,否则报错。防止自己配置错误
        }

        private IntegerCache() {}
    }

代码中都加了注解,应该很好理解。
看到这里应该就很明白了:得出结论
(1)System.out.println(a==b);中,因为128>127所以,在自动装箱执行valueOf方法时时并没有进入if语句,而是创建的新的Integer对象,所以显示a、b是两个对象的引用,所以输出false。
(2)同理 System.out.println(c==d);中,1<127,所以进入if语句块,c、d对象都是IntegerCache.cache[1]引用的对象,所以自然是相等的!输出true。

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
public static void main(String[] args) {
        Integer a = 128;
        Integer b = 128;
        Integer c = 1;
        Integer d = 1;
        System.out.println(a==b);
        System.out.println(a.equals(b));
        System.out.println(c==d);
        System.out.println(c.equals(d))
}
//输出:
false
true
true
true

到了这里其实还没有完:如下代码会输出什么呢?

public static void main(String[] args) {
        Integer e = new Integer(128);
        Integer f = new Integer(128);
        Integer g = new Integer(1);
        Integer h = new Integer(1);
        
        System.out.println(e==f);
        System.out.println(e.equals(f));
        System.out.println(g==h);
        System.out.println(g.equals(h)); 
    }

使用new创建对象是不会触发自动装箱操作的,所以预测会打印false、true、false、true,执行结果如下,和预料的一样。

false
true
false
true

到这里所有关于Integer对象的比较问题就都说清楚了,但是还会有a与e比较、c与g比较的问题,这些衍生问题就很容易了。

最后再总结一下:==比较的地址,equals方法得看代码怎么写(一般是值的比较)

that is all!!!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值