引入

没有前戏,直奔代码,来看下面这段代码的运行结果:

@Test
public void test() {
    Integer i = 666;
    int j = i + 1;
    System.out.println("j = " + j);
}

自然,我们都知道会打印 j = 667,曾经我很好奇,i是Integer对象,属于包装类型,而j是int基础数据类型,他俩怎么会在一起运算呢?直到我扒开Integer的外表,直接看到了他的内涵…

当然我是借助于工具看到程序运行的内涵的,IDEA的jclasslib Bytecode viewer这个插件,能够反编译代码,而且,反编译出来的指令,可以直接链接到官网上查看。

安装好插件后,如图所示,就可以看反编译后的代码指令了:

Java比较两个int不相等 java两个integer比较_java

上面这段程序的字节码指令:

Java比较两个int不相等 java两个integer比较_IntegerCache_02

那么这些都是什么玩意呢?

自动装箱与自动拆箱

自动装箱(auto boxing)和自动拆箱(auto unboxing)是Java 5引入的功能,有了这两个功能,Java在编译阶段,会根据上下文对数据类型自动进行转换,可以保证不同的写法在运行时等价。

自动装箱:将值类型装换成引用类型的过程

自动拆箱:将引用类型转换成值类型的过程

Integer i = 666;
int j = i + 1;

这两行代码就是就体现了自动装箱与自动拆箱。

来看一下代码编译后的字节码指令:

0 sipush 666
 3 invokestatic #2 <java/lang/Integer.valueOf>
 6 astore_1
 7 aload_1
 8 invokevirtual #3 <java/lang/Integer.intValue>
11 iconst_1
12 iadd
13 istore_2
14 return

第3行:
invokestatic #2 <java/lang/Integer.valueOf>

Java比较两个int不相等 java两个integer比较_缓存_03

意思是调用类的静态方法,后面指出了是调用Integer的valueOf这个静态方法,也就是说在编译阶段Java就自动把装箱转换成了Integer.valueOf

第8行:invokevirtual #3 <java/lang/Integer.intValue>

Java比较两个int不相等 java两个integer比较_Integer_04

调用类实例的方法,这个拆箱就是说在编译阶段就调用了Integer的intValue方法。

分别来看一下valueOfintValue这两个方法源码。

valueOf:

/**
* 返回表示指定int值的整数实例。如果不需要新的Integer实例,
* 那么通常应该优先使用该方法,而不是构造函数Integer(int),
* 因为通过缓存经常请求的值,该方法可能会产生更好的空间和时间性能。
* 此方法将始终缓存范围为(-128,127]的值,并可能缓存此范围之外的其他值。
*/
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

水越来越深了,这时又跑出来个IntegerCache

IntegerCache:

/**
 * 缓存支持自动装箱为-128,并根据需要通过JLS 127(含)之间的值的对象标识语义。
 * 缓存是在第一次使用初始化的。
 * 缓存的大小可以通过-XX:AutoBoxCacheMax=<size>选项设置。
 * 在虚拟机初始化的过程中,在系统类sun.misc.VM中设置并保存java.lang.Integer.IntegerCache.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() {}
}

这就是所谓的Integer缓存,在虚拟机初始化的过程中,就已经缓存好了(-128,127]之间的数据(自动装箱)。

再看一下intValue()方法:

//...
// The value of the {@code Integer}.
int private final int value;
//...

/**
* 以整型数的形式返回该Integer的值
*/
public int intValue() {
    return value;
}

以整型数的形式返回该Integer的值,对应拆箱。

比较两个Integer的值

看代码:

@Test
public void test() {
    Integer i1 = 66;
    Integer i2 = 66;
    System.out.println("66 == 66 ? " + (i1 == i2));
    Integer i3 = 666;
    Integer i4 = 666;
    System.out.println("666 == 666 ? " + (i3 == i4));
}

到这大家肯定都知道一个是true,一个是false:

Java比较两个int不相等 java两个integer比较_Integer_05

但是,这是为什么呢?被问到这个丝毫不慌,其实前文已经解释了,Integer里面搞了个IntegerCache这个东西,它默认缓存了(-128,127]之间的数据(可以通过 -XX:AutoBoxCacheMax= 设置),并用数组Integer cache[]保存起来了,也就是说在(-128,127]之间的数值都是IntegerCache.cache[] 数组中的同一个Integer对象。

66在(-128,127]之间,666大于127了,所以i1 == i2为true,而i3 == i4为false。

避坑

那么怎么正确的比较两个Integer的值呢?用equals()!

Java比较两个int不相等 java两个integer比较_IntegerCache_06

equals:

/**
* 将此对象与指定对象进行比较。
* 当且仅当参数不为null且为包含与此对象相同整型值的整数对象时,结果为真。
*/
public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}

哈哈,equals方法比较的是两个对象的整型值,不用考虑是基础类型还是引用类型了,一律转换成int类型再进行比较!

这也就是阿里Java开发手册上说的强制使用equals方法比较整型包装类对象的值

Java比较两个int不相等 java两个integer比较_Java比较两个int不相等_07