在 Java 的世界里,包装类就像连接基本数据类型与对象世界的桥梁。你有没有遇到过这样的怪事:同样是创建 Integer 对象,有时用 == 比较返回 true ,有时却返回 false ?这背后藏着 Java 包装类的一个聪明优化 —— 缓存机制 。今天,我们就来揭开它的面纱!
一、缓存机制是什么?
简单说,缓存机制就是 Java 提前为一些常用数值创建好对象,存在"仓库"里。当你需要用这些值时,不用重新造新对象,直接从"仓库"里拿现成的。这就像餐厅提前准备好常用调料,厨师做饭时不用现买,既省时间又省钱。
二、哪些包装类有缓存?
不是所有包装类都有这个待遇,主要集中在数值型和布尔型:
包装类 缓存范围 特点说明 Boolean true 和 false 只有两个值,全部缓存 Byte -128 ~ 127 取值范围小,全部缓存 Short -128 ~ 127 常用小范围数值 Integer -128 ~ 127 可通过 JVM 参数调整上限 Long -128 ~ 127 常用小范围数值 Character 0 ~ 127 ASCII 码字符,文本处理常用 Float/Double 无缓存 取值范围广,离散分布,缓存意义不大
三、缓存机制的工作原理
以我们最熟悉的 Integer 为例,它的缓存是通过一个叫 IntegerCache 的内部静态类实现的。当 Integer 类加载时, IntegerCache 会预先创建 -128~127 范围内的所有 Integer 对象,存到一个数组里。
当你用 Integer.valueOf(int i) 方法(自动装箱时也会调用这个方法)获取对象时:
- 如果 i 在缓存范围内,直接从数组里拿现成对象
- 如果 i 超出范围,才会创建新对象 看个直观的例子:
// 自动装箱,调用Integer.valueOf()
Integer a = 100; // 从缓存拿对象
Integer b = 100; // 从缓存拿同一个对象
System.out.println(a == b); // true,地址相同
Integer c = 200; // 超出缓存范围,创建新对象
Integer d = 200; // 超出缓存范围,创建另一个新对象
System.out.println(c == d); // false,地址不同
Boolean 的缓存更简单,直接存了两个常量对象对应 true 和 false ,所以无论你获取多少次,得到的都是同一个对象。
四、使用缓存的注意事项
1. 区分 == 和 equals()
== 比较的是对象的地址, equals() 比较的是对象的值。有缓存时,用 == 可能会出现"奇怪"的结果:
Integer a = 127; // 缓存内
Integer b = 127; // 缓存内
System.out.println(a == b); // true(地址相同)
System.out.println(a.equals(b)); // true(值相同)
Integer c = 128; // 缓存外
Integer d = 128; // 缓存外
System.out.println(c == d); // false(地址不同)
System.out.println(c.equals(d)); // true(值相同)
结论 :比较包装类的值时,一定要用 equals() ,别用 == !
2. new 关键字不触发缓存
即使值在缓存范围内,用 new 创建的对象也不会用缓存:
Integer a = 100; // 用缓存
Integer b = new Integer(100); // 新对象
System.out.println(a == b); // false
3. 调整 Integer 缓存范围
可以通过 JVM 参数 -XX:AutoBoxCacheMax= 调整 Integer 缓存的上限(比如 -XX:AutoBoxCacheMax=500 ),但下限 -128 是固定的。
五、缓存机制的意义
六、总结
包装类的缓存机制是 Java 对常用数据的一种"复用"优化。就像我们生活中会把常用物品放在随手可得的地方,Java 也会把常用数值提前准备好,用的时候直接拿。记住:
- 比较包装类值用 equals()
- 缓存范围有限制
- new 出来的对象不共享缓存