Integer陷阱

2017-12-04  本文已影响47人  heyong

概述

今天同事遇到一个关于 Integer 的坑:有两个 Integer 对象,Integer i = 10000,Integer j = 10000,判断两个对象是否相等。Java里面基本类型都有对应的对象,并且回进行自动装箱和拆箱的操作,就误以为对象 i 和对象 j 是比较的整形值,但是结果确出乎意料。

参考代码

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

        System.out.println(a == b);
        Integer i = 100001;
        Integer j = 100001;

        System.out.println(i == j);
    }
}

运行结果为:

true
false

本以为输出结果应该是两个true,但是最终的答案出乎意料,于是查看编译以后Test类对应的字节码命令,字节码命令如下:

javap -v Test.class

得到的字节码命令如下:

public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=5, args_size=1
         0: bipush        100
         2: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         5: astore_1      
         6: bipush        100
         8: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        11: astore_2      
        12: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        15: aload_1       
        16: aload_2       
        17: if_acmpne     24
        20: iconst_1      
        21: goto          25
        24: iconst_0      
        25: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
        28: ldc           #5                  // int 100001
        30: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        33: astore_3      
        34: ldc           #5                  // int 100001
        36: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        39: astore        4
        41: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        44: aload_3       
        45: aload         4
        47: if_acmpne     54
        50: iconst_1      
        51: goto          55
        54: iconst_0      
        55: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
        58: return        

通过上面的字节码命令可以看出,Integer 的装箱是通过 invokestatic 指令,调用 Integer.valueOf() 方法实现的。对两个对象进行比较时,是通过 if_acmpne 指令比较两个Integer对象,接下来我们看看 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);
    }

IntegerCache.low = -128, IntegerCache.high = 127,在Integer中将 -128 到 127 对应的 Integer 对象缓存到 IntegerCache中,如果给 Integer对象赋值 >= -128 或者 赋值 <= 127,就从缓存中获取,如果超出了 -128 - 127的范围,就在堆中创建一个 Integer 对象,感兴趣的同学可以用 HSDB 工具查看。

if_acmpne 指令会进行两个对象的比较,如果内存地址不一样就返回 false,这就解释了上面的情况,所以比较两个对象的时候还是调用equals方法靠谱

上一篇下一篇

猜你喜欢

热点阅读