通过本篇,你会了解到Int和Integer的比较,了解Integer的缓存机制。
1. 题目
@Test
void integer_test() {
Integer a1 = new Integer(128);
Integer a2 = new Integer(128);
int b1 = 128;
int b2 = 128;
Integer c1 = 128;
Integer c2 = 128;
Integer d1 = 127;
Integer d2 = 127;
System.out.println("1.1 = " + (a1 == a2)); // false
System.out.println("1.2 = " + (b1 == b2)); // true
System.out.println("1.3 = " + (c1 == c2)); // false
System.out.println("1.4 = " + (d1 == d2)); // true
System.out.println("2.1 = " + (a1 == b1)); // true
System.out.println("2.2 = " + (a1 == c1)); // false
System.out.println("2.3 = " + (b1 == c1)); // true
}
2. 解释
- Integer 是 int 的包装类,Integer 的默认值是 null,int 的默认值是 0;
- 在 new 一个 Integer 时,会重新生成一个指针指向该对象,所以:a1 != a2;
- Integer 是包装类,属于对象, Integer c1 = 128 <=> Integer c1 = new Integer(128);所以:a1 != c1,c1 != c2;
- Java 中 Integer 类型对于(-128-127)之间的数会写入Cache缓存,再次被定义时就会直接从缓存中取,所以用等号比较是一致的。比如:Integer d1 = 127 时会将127进行缓存,下次再定义 Integer d2 = 127 时,d2会直接从缓存中取,但是 c1 和 c2 的 128 不会被缓存,所以:c1 != c2,d1 == d2;
- Java 在编译的时候,Integer c1 = 128;会被翻译成:Integer c1 = Integer.valueOf(128);
- int 与 Integer 比较的时候发生自动拆箱,将 Integer 自动变为 int,不管值为多少始终是相等的,所以:a1 == b1,b1 == c1;
2.1 Integer的缓存问题
一个字节 8 位,即可表示 2^8 = 256,有符号数那就是 -128 ~ 128 了,还要考虑去除一个符号位,实际表示的范围是 -128 ~ 127,也就是说:Java 中 Integer 类型在 Cache 中会默认缓存一个字节以内的值。
上面讲到了:Integer c1 = Integer.valueOf(128);看一下 valueOf() 方法内部是怎么缓存的?源码如下:
public final class Integer extends Number implements Comparable<Integer> {
// ...
/**
* Returns an {@code Integer} instance representing the specified
* {@code int} value. If a new {@code Integer} instance is not
* required, this method should generally be used in preference to
* the constructor {@link #Integer(int)}, as this method is likely
* to yield significantly better space and time performance by
* caching frequently requested values.
*
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
// ...
}
根据源码注释可知:
Integer.value() 会返回一个指定的 int 值的 Integer 实例。
若不需要一个新的 Integer 实例,会优先采用该方法实例化,而不是 new Integer(int i)。因为相比于使用 new Integer(int i) 的方法实例化对象,valueOf(int i) 会缓存经常请求的值 (多次请求会从缓存中获得),从而获得更好的空间和时间性能。
通过阅读源码,发现 Integer 类中存在一个静态内部类 IntegerCache,源码如下:
/**
* Cache to support the object identity semantics of autoboxing for values between
* -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.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() {}
}
根据源码上方的注释可知:
该类默认缓存值在 [-128, 127] 内的 Integer 对象,且采用一个 final 修饰的数组 cache 作为存储容器,首次使用时会初始化该缓存。
此外,缓存的上限是可以修改的,通过修改选项:-XX:AutoBoxCacheMax= 来设置缓存上限值。
3. Integer比较的方法
Integer 是 int 的包装类,相同类型的包装类对象之间的值比较,最好使用 equals。
System.out.println("3.1 = " + (c1.equals(c2))); // true
equals 底层原理其实就是:intValue();