目录
一、前言简介
本文介绍Java 中 `Integer` 类的一个核心特性——整数缓存机制(Integer Cache)。Java 的 Integer 类通过静态缓存池,默认预先创建并复用了数值在 -128 到 127 之间的对象,以提高空间和时间效率。
二、详细说明
1. 示例代码与解析
无需多言,直接上代码。
Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true
Integer c = 128;
Integer d = 128;
System.out.println(c == d); // false
-
`a == b` 为 `true`:因为 `127` 在默认的缓存范围 `-128~127` 之内。当你使用 `Integer a = 127;` 这种写法时,Java 会自动调用 `Integer.valueOf(127)` 来获取对象。`valueOf()` 方法会优先从缓存池(cache)中返回一个已经存在的、值相同的 `Integer` 对象。因此,`a` 和 `b` 指向的是同一个对象,`==` 比较的是引用地址,所以结果为 `true`。
-
`c == d` 为 `false`:因为 `128` 超出了默认的缓存范围。`Integer.valueOf(128)` 在缓存池中找不到对应的对象,于是会在堆内存中创建一个新的 `Integer` 对象。因此,`c` 和 `d` 指向的是两个不同的、但值相同的对象。`==` 比较它们的引用地址,自然不相等,返回 `false`。
2. `Integer.valueOf()` vs `new Integer()`
这是理解这个问题的关键。
`Integer.valueOf(int i)`:这是一个静态工厂方法。它的核心逻辑就是使用缓存。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i); // 只有不在缓存范围内,才 new 新对象
}
所以,`valueOf()` 不一定创建新对象。
`new Integer(int i)`:这是一个构造函数调用。它永远会在堆上创建一个全新的对象,完全无视缓存范围。
Integer e = new Integer(127);
Integer f = new Integer(127);
System.out.println(e == f); // false,因为是两个不同的对象
System.out.println(e.equals(f)); // true,因为值相等
3. 自动装箱(Autoboxing)
Java 5 引入了自动装箱/拆箱机制。当我们写 `Integer a = 127;` 时,编译器会自动将其转换为 `Integer a = Integer.valueOf(127);`。
所以,`Integer a = 127;` 在底层完全等价于 `Integer a = Integer.valueOf(127);`。这就是为什么它会受到缓存机制的影响。
4. `==` 和 `equals()` 的区别
这是问题的另一个核心。
`==` 操作符:对于对象类型,它比较的是两个变量指向的内存地址是否相同(即是否是同一个对象)。它不关心对象内部的值。
`equals()` 方法:这个方法由类自己实现,用于比较两个对象的逻辑内容是否相等。`Integer` 类重写了 `equals()` 方法,使其比较的是包装的 `int` 值。
正确比较两个 `Integer` 对象值的方法永远是使用 `equals()`。
Integer c = 128;
Integer d = 128;
System.out.println(c == d); // false(比较地址,不推荐)
System.out.println(c.equals(d)); // true(比较值,正确!)
5. 缓存范围的调整
缓存的上限 `127` 是默认值,但并非不可改变。缓存的上限是通过 JVM 的启动参数来设置的。
-
`-XX:AutoBoxCacheMax=<size>`:你可以通过这个 JVM 参数来调整 `Integer` 缓存的最大值。
-
例如,使用 `java -XX:AutoBoxCacheMax=500 YourApp` 启动程序,那么缓存范围就会变成 `-128~500`。
-
注意:这个参数只调整上限(high),下限(low)始终是 -128。
-
这个参数对其他整型包装类(如 `Long`, `Short`)的缓存没有影响。
6. 其他包装类的缓存
不仅 `Integer`,其他包装类也有缓存机制:
-
`Byte`:全部缓存(-128~127,一共256个值)。因为 byte 的范围就是这么多。
-
`Short`:缓存 -128~127。
-
`Long`:缓存 -128~127。
-
`Character`:缓存 0~127(ASCII/Unicode 字符)。
-
`Boolean`:缓存 `TRUE` 和 `FALSE` 两个实例(全部缓存)。
-
`Float` 和 `Double`:没有缓存。`valueOf` 方法总是返回新对象。
三、对比总结
操作 | 是否使用缓存 | 说明 |
---|---|---|
Integer a = 100; (自动装箱) | 是 | 底层调用 Integer.valueOf(100) |
Integer.valueOf(100) | 是 (如果在范围内) | 优先从缓存返回对象 |
new Integer(100) | 否 | 始终创建新对象 |
== 比较 | - | 比较对象地址,结果不可预测,不要用于值比较 |
equals() 比较 | - | 比较包装的值,是正确比较内容的方法 |
-
永远使用 `equals()` 来比较两个包装类对象的值。
-
理解 `==` 在对象比较时的行为,避免踩坑。