问题
两个 Integer 对象比较时,为什么 100 等于 100 而 1000 不等于 1000?
答案
核心结论
这是因为 Integer 缓存池机制:
- Java 会缓存 -128 到 127 之间的 Integer 对象
- 在这个范围内,
Integer.valueOf()返回的是同一个对象 - 超出范围则每次创建新对象
因此:
Integer a = 100; Integer b = 100;→a == b为 true(同一对象)Integer c = 1000; Integer d = 1000;→c == d为 false(不同对象)
代码验证
public class IntegerCacheDemo {
public static void main(String[] args) {
// 范围内:使用缓存对象
Integer a = 100;
Integer b = 100;
System.out.println("a == b: " + (a == b)); // true
System.out.println("a.equals(b): " + a.equals(b)); // true
// 范围外:创建新对象
Integer c = 1000;
Integer d = 1000;
System.out.println("c == d: " + (c == d)); // false
System.out.println("c.equals(d): " + c.equals(d)); // true
// 边界测试
Integer e1 = -128;
Integer e2 = -128;
System.out.println("e1 == e2: " + (e1 == e2)); // true
Integer f1 = 127;
Integer f2 = 127;
System.out.println("f1 == f2: " + (f1 == f2)); // true
Integer g1 = 128;
Integer g2 = 128;
System.out.println("g1 == g2: " + (g1 == g2)); // false
// 使用 new 关键字:总是创建新对象
Integer h1 = new Integer(100);
Integer h2 = new Integer(100);
System.out.println("h1 == h2: " + (h1 == h2)); // false
System.out.println("h1.equals(h2): " + h1.equals(h2)); // true
}
}
原理分析
1. 自动装箱机制
// 自动装箱(编译器自动转换)
Integer a = 100;
// 等价于
Integer a = Integer.valueOf(100);
2. 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);
}
3. IntegerCache 内部类
private static class IntegerCache {
static final int low = -128;
static final int high; // 默认 127,可通过 JVM 参数调整
static final Integer cache[];
static {
// 可以通过 JVM 参数 -XX:AutoBoxCacheMax=<size> 调整上限
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); // 最小为 127
h = Math.min(i, Integer.MAX_VALUE - (-low) - 1);
} catch (NumberFormatException nfe) {
// 忽略异常
}
}
high = h;
// 创建缓存数组
cache = new Integer[(high - low) + 1];
int j = low;
for (int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// 断言确保缓存正确
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
为什么是 -128 到 127?
- 常用范围:这个范围覆盖了大部分日常使用的整数
- 性能优化:避免频繁创建小整数对象,节省内存
- 历史原因:byte 类型的范围就是 -128 到 127
其他包装类的缓存机制
| 包装类 | 缓存范围 | 是否可调整 |
|---|---|---|
| Integer | -128 ~ 127 | ✅ 可通过 JVM 参数调整上限 |
| Long | -128 ~ 127 | ❌ 固定 |
| Short | -128 ~ 127 | ❌ 固定 |
| Byte | -128 ~ 127 | ❌ 固定(全部缓存) |
| Character | 0 ~ 127 | ❌ 固定 |
| Boolean | true, false | ❌ 固定(只有两个对象) |
| Float | ❌ 无缓存 | - |
| Double | ❌ 无缓存 | - |
代码验证其他包装类
public class WrapperCacheDemo {
public static void main(String[] args) {
// Long 缓存
Long l1 = 100L;
Long l2 = 100L;
System.out.println("Long 100: " + (l1 == l2)); // true
Long l3 = 1000L;
Long l4 = 1000L;
System.out.println("Long 1000: " + (l3 == l4)); // false
// Character 缓存
Character c1 = 'A'; // ASCII 65
Character c2 = 'A';
System.out.println("Char A: " + (c1 == c2)); // true
Character c3 = '中'; // 超出 127
Character c4 = '中';
System.out.println("Char 中: " + (c3 == c4)); // false
// Boolean 缓存(只有两个对象)
Boolean b1 = true;
Boolean b2 = true;
System.out.println("Boolean true: " + (b1 == b2)); // true
// Double 无缓存
Double d1 = 100.0;
Double d2 = 100.0;
System.out.println("Double 100.0: " + (d1 == d2)); // false
}
}
调整 Integer 缓存上限
可以通过 JVM 参数调整缓存上限(但不能调整下限):
# 将缓存上限调整为 1000
java -XX:AutoBoxCacheMax=1000 IntegerCacheDemo
public class CustomCacheDemo {
public static void main(String[] args) {
// 如果设置了 -XX:AutoBoxCacheMax=1000
Integer a = 1000;
Integer b = 1000;
System.out.println("1000 == 1000: " + (a == b)); // 可能为 true
}
}
常见陷阱与最佳实践
陷阱 1:混淆 == 和 equals
Integer a = 100;
Integer b = 100;
Integer c = 1000;
Integer d = 1000;
// ❌ 错误:使用 == 比较对象
System.out.println(a == b); // true(碰巧相等)
System.out.println(c == d); // false(不相等)
// ✅ 正确:使用 equals 比较值
System.out.println(a.equals(b)); // true
System.out.println(c.equals(d)); // true
陷阱 2:自动装箱与手动创建
Integer a = 100; // 自动装箱,使用缓存
Integer b = Integer.valueOf(100); // 使用缓存
Integer c = new Integer(100); // 手动创建,不使用缓存
System.out.println(a == b); // true
System.out.println(a == c); // false
陷阱 3:空指针异常
Integer a = null;
int b = a; // NullPointerException(自动拆箱)
// 安全做法
Integer a = null;
int b = (a != null) ? a : 0;
最佳实践
public class BestPractice {
public static void main(String[] args) {
Integer a = 100;
Integer b = 1000;
// ✅ 推荐:使用 equals 比较包装类
if (a.equals(b)) {
System.out.println("相等");
}
// ✅ 推荐:使用 Objects.equals 避免空指针
Integer c = null;
if (Objects.equals(a, c)) {
System.out.println("相等");
}
// ✅ 推荐:基本类型比较用 ==
int x = 100;
int y = 100;
if (x == y) {
System.out.println("相等");
}
// ❌ 不推荐:使用 new Integer()(已过时)
Integer d = new Integer(100); // 编译警告
// ✅ 推荐:使用 Integer.valueOf()
Integer e = Integer.valueOf(100);
}
}
面试要点总结
- 缓存范围:
- Integer 缓存 -128 到 127
- 可通过
-XX:AutoBoxCacheMax调整上限
- 原理:
- 自动装箱调用
Integer.valueOf() - 范围内返回缓存对象,范围外创建新对象
- 自动装箱调用
- 比较规则:
==比较对象引用(地址)equals()比较对象值- 包装类必须用 equals 比较
- 其他包装类:
- Long、Short、Byte、Character 也有缓存
- Float、Double 无缓存
- 最佳实践:
- 包装类比较用
equals()或Objects.equals() - 避免使用
new Integer()(已过时) - 注意自动拆箱的空指针风险
- 包装类比较用
// 记忆口诀
// -128 到 127,Integer 有缓存
// 范围内相等,范围外不等
// 包装类比较,equals 最安全
这道题考察的是对 Java 自动装箱、缓存池机制和对象比较的深入理解,是面试中的经典题目。