前言
上篇文章讲的是基本数据类型,本章其实是从上一篇文章中抽出来的,上文篇幅过大,所以就把这部分内容单独抽出来了。
本文主要分析 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: false
c == d: true
e == 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 property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.hig
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() {}
}
根据注释描述,我们知道 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: true
a == 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 : true
a.equals(b): true
c == d: false
c.equals(d): true
e == f: false
e.equals(f): true
总结
这种看似很小的问题也不能忽视,代码执行正确与否,很多时候在于细节的处理。
上面有些内容是结论性的,没有太过讲细,有些东西我也不太清楚,比如为什么当 int 类型和它的引用类型 Integer 比较的时候,Integer 会自动拆箱?这个应该和编译后的字节码有关了,需要分析编译后的 class 文件。
作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/vxobtk 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。