前言
上篇文章讲的是基本数据类型,本章其实是从上一篇文章中抽出来的,上文篇幅过大,所以就把这部分内容单独抽出来了。
本文主要分析 Integer 和 int 之间的比较问题,接下来慢慢展开。
版本约定
- JDK 版本:1.8.0_231
 - Java SE API Documentation:https://docs.oracle.com/javase/8/docs/api/
正文
引用类型缓存池
 
我们先来看一段代码,猜猜它的执行结果是什么样子?
public static void main(String[] args) {Integer a = new Integer(123);Integer b = new Integer(123);System.out.println("a == b: " + (a == b));Integer c = Integer.valueOf(123);Integer d = Integer.valueOf(123);System.out.println("c == d: " + (c == d));Integer e = Integer.valueOf(128);Integer f = Integer.valueOf(128);System.out.println("e == f: " + (e == f));}
运行程序,输出:
a == b: falsec == d: truee == f: false
这里的执行结果和我们想象的结果有出入,有两点疑问:
- 为什么 new Integer(123) 和 Integer.valueOf(123) 的执行结果不一样?
 - 为什么 Integer.valueOf(123) 和 Integer.valueOf(128) 的执行结果不一样?
 
带着这两个问题,我们来看看 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 是什么?看看它的源码内容。
/*** Cache to support the object identity semantics of autoboxing for values b* -128 and 127 (inclusive) as required by JLS.** The cache is initialized on first usage. The size of the cache* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.* During VM initialization, java.lang.Integer.IntegerCache.high property* may be set and saved in the private system properties in the* sun.misc.VM class.*/private static class IntegerCache {static final int low = -128;static final int high;static final Integer cache[];static {// high value may be configured by propertyint h = 127;String integerCacheHighPropValue =sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.higif (integerCacheHighPropValue != null) {try {int i = parseInt(integerCacheHighPropValue);i = Math.max(i, 127);// Maximum array size is Integer.MAX_VALUEh = 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() {}}
根据注释描述,我们知道 IntegerCache 是 Integer 的一个内部缓存池,默认缓存 -128 到 127 的整数,另外描述中还讲到自动装箱的概念,下面会提到。
我们再看 valueOf() 方法就知道了,它的实现比较简单,就是先判断值是否在缓存池中,如果在的话就直接返回缓存池的内容。
这样,上面的两个问题,你应该就知道原因了吧。
接下来,我们来看上面提到的自动封装的概念,下面的代码,大家一定不会陌生。
public static void main(String[] args) {Integer a = 1;System.out.println(a);}
我们经常这样初始化 Integer 类型,编译器在把 int 类型的数据封装成 Integer 类型的操作就叫做自动装箱。
编译器会在自动装箱过程中会调用 valueOf() 方法,因此多个 Integer 实例使用自动装箱来创建并且值相同(范围:-128 ~ 127),那么就会引用相同的对象。
测试代码:
public static void main(String[] args) {Integer a = 123;Integer b = 123;System.out.println("a == b: " + (a == b));}
运行程序,输出:
a == b: true
其他基本类型对应的引用类型是否也存在缓存池呢?它的缓存范围是多少呢?总结如下:
- boolean values true and false
 - all byte values
 - short values between -128 and 127
 - int values between -128 and 127
 - char in the range \u0000 to \u007F
Integer 和 int 的比较
 
结合上面的源码分析,我们可以得出如下结论:
- 当 int 类型和它的引用类型 Integer 比较的时候,Integer 会自动拆箱,转换为 int 类型进行比较,所以可以直接使用 
**==**进行比较是否相等。 
测试代码:
public static void main(String[] args) {int a = 200;Integer b = 200;Integer c = new Integer(200);System.out.println("a == b: " + (a == b));System.out.println("a == c: " + (a == c));}
运行程序,输出:
a == b: truea == c: true
- 当是两个引用类型 Integer 比较的时候,使用 equals 方法进行比较,否则容易出现意想不到的结果。
 
测试代码:
public static void main(String[] args) {Integer a = 127;Integer b = 127;System.out.println("a == b : " + (a == b));System.out.println("a.equals(b): " + a.equals(b));Integer c = 128;Integer d = 128;System.out.println("c == d: " + (c == d));System.out.println("c.equals(d): " + c.equals(d));Integer e = 127;Integer f = new Integer(127);System.out.println("e == f: " + (e == f));System.out.println("e.equals(f): " + e.equals(f));}
运行程序,输出:
a == b : truea.equals(b): truec == d: falsec.equals(d): truee == f: falsee.equals(f): true
总结
这种看似很小的问题也不能忽视,代码执行正确与否,很多时候在于细节的处理。
上面有些内容是结论性的,没有太过讲细,有些东西我也不太清楚,比如为什么当 int 类型和它的引用类型 Integer 比较的时候,Integer 会自动拆箱?这个应该和编译后的字节码有关了,需要分析编译后的 class 文件。
作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/vxobtk 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
