Integer 拆装箱与缓存机制:为什么 2 == 2 而 200 != 200?
在 Java 中,Integer 作为 int 的包装类,其拆装箱(Autoboxing/Unboxing)机制和缓存策略常常导致看似矛盾的结果,比如两个值相等的 Integer 对象用 == 比较时,有时为 true,有时为 false。本文将深入解析这一现象背后的原理。
自动拆装箱的基本概念
自动装箱(Autoboxing):将基本数据类型(如 int)自动转换为对应的包装类(如 Integer)。
自动拆箱(Unboxing):将包装类(如 Integer)自动转换为对应的基本数据类型(如 int)。
示例:
1 | // 自动装箱:int → Integer |
正是自动装箱机制,使得 Integer a = 2 这种语法成立,而其内部依赖 Integer.valueOf(int) 方法实现。
Integer 缓存机制(IntegerCache)
Integer 的缓存机制是导致 2 == 2 而 200 != 200 的核心原因。Java 为了优化性能,对 -128 到 127 之间的整数 进行了缓存,避免频繁创建相同值的 Integer 对象。
1. Integer.valueOf(int) 方法的实现
Integer.valueOf(int) 是自动装箱的核心方法,其源码逻辑如下:
1 | public static Integer valueOf(int i) { |
2. IntegerCache 内部类
IntegerCache 是 Integer 的静态内部类,负责管理缓存的 Integer 实例:
1 | private static class IntegerCache { |
关键特性:
- 缓存范围默认是
-128 ~ 127; - 缓存上限
high可通过 JVM 参数-XX:AutoBoxCacheMax=xxx调整(如调整为 500),但下限-128不可修改; - 缓存数组在类加载时初始化,所有在范围内的
Integer实例被提前创建并存储,后续直接复用。
题目解析:为什么 a == b 为 true 而 c == d 为 false?
回到开头的题目:
1 | Integer a = 2; // 自动装箱:调用 Integer.valueOf(2) |
原因分析:
a和b的比较:2在-128 ~ 127范围内,Integer.valueOf(2)会返回缓存中的同一个Integer实例。因此a和b指向同一个对象,a == b为true。c和d的比较:200超出了默认缓存范围(>127),Integer.valueOf(200)会创建新的Integer实例。因此c和d指向不同的对象,c == d为false。
拓展:其他包装类的缓存机制
Java 中其他基本类型的包装类也有类似的缓存机制,但范围和实现略有不同:
| 包装类 | 缓存范围 | 特点 |
|---|---|---|
Byte |
-128 ~ 127 |
范围固定,不可修改 |
Short |
-128 ~ 127 |
范围固定,不可修改 |
Character |
0 ~ 127 |
范围固定,缓存 ASCII 字符 |
Long |
-128 ~ 127 |
范围固定,不可修改 |
Integer |
-128 ~ 127(可调整上限) |
唯一可通过 JVM 参数修改缓存上限的类 |
Boolean |
true 和 false |
仅缓存两个静态实例 |
注意事项与最佳实践
==与equals()的区别:==比较对象引用(地址),仅当两个Integer指向同一实例时为true;equals()比较值,无论是否在缓存范围内,只要值相等就为true。
示例:
1
2
3
4Integer a = 200;
Integer b = 200;
System.out.println(a == b); // false(引用不同)
System.out.println(a.equals(b)); // true(值相同)避免依赖缓存范围:
缓存上限可通过 JVM 参数修改,因此不要在代码中假设Integer的缓存范围是固定的127,比较值时优先使用equals()。拆箱后的比较:
若Integer与int比较,Integer会自动拆箱为int,此时==比较的是值:1
2
3Integer a = 200;
int b = 200;
System.out.println(a == b); // true(a 自动拆箱为 int,比较值)