[Java学习日记13】聊聊自动装箱与自动拆箱
🚀 目录
-
-
4.1 为什么是 -128 到 127?
-
4.2 哪些包装类拥有“特权”?
-
-
-
5.1 循环中的“内存杀手”
-
5.2 自动拆箱引发的 NullPointerException
-
5.3 混合运算的诡异逻辑
-
1. 开篇:一个让全寝室破防的“灵异事件”
坐标:图书馆 3 楼(空调漏水,我挪了三个位置)
状态:冰美式已经成了常温美式,代码运行结果让我怀疑人生。
碎碎念: 兄弟们,今天我彻底傻了。 刚才在复习基础的时候,随手写了两行代码:
Integer a = 100; Integer b = 100; System.out.println(a == b); // 结果:true Integer c = 128; Integer d = 128; System.out.println(c == d); // 结果:false ???看到结果的一瞬间,我以为我的 JDK 中毒了,或者是图书馆的磁场干扰。同样的写法,换个数字就不相等了?
隔壁桌的学霸看了一眼,淡淡地回了一句:“这叫 IntegerCache 机制,你对自动装箱的底层力量一无所知。”
痛定思痛,我翻开了《Java 虚拟机规范》和源码。今天咱就来扒一扒这层“皮”,看看 Java 到底在背后搞了什么鬼!
2. 基础概念:包装类与基本类型的“变身术”
在 Java 里,万物皆对象,但偏偏有 8 个“异类”——基本数据类型(int, double, char 等)。为了让它们能进集合(比如 ArrayList<Integer>),Java 提供了对应的包装类。
-
自动装箱:基本类型 -> 包装类(
int到Integer)。 -
自动拆箱 :包装类 -> 基本类型(
Integer到int)。
这看起来很智能,但正是这种“自动”,隐藏了巨大的性能开销和逻辑陷阱。
3. 底层真相:字节码里的 valueOf 与 intValue
我们看到的:
Integer i = 10; // 装箱
int n = i; // 拆箱
JVM 看到的(通过 javap -c 反编译):
-
装箱时调用了:
Integer.valueOf(int i) -
拆箱时调用了:
Integer.intValue()
划重点:所谓的自动,全是编译器的“骚操作”。它在编译阶段偷偷帮我们补全了方法调用。
4. 核心天坑:IntegerCache 缓存池的秘密
回到开头的“灵异事件”。为什么 100 相等,128 就不相等了?
4.1 为什么是 -128 到 127?
去翻 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);
}
真相大白:Java 为了优化性能,预先创建了一个缓存数组,存放了 -128 到 127 之间的对象。
-
当你赋值 100 时,拿的是缓存里的同一个对象,地址当然一样,
==返回true。 -
当你赋值 128 时,由于超出了缓存范围,Java 每次都会
new一个新对象。地址不同,==自然返回false。
4.2 哪些包装类拥有“特权”?
-
Byte,Short,Integer,Long:全部缓存了 -128 到 127。 -
Character:缓存了 0 到 127。 -
Boolean:直接缓存了TRUE和FALSE两个常量。 -
注意:
Float和Double并没有缓存,因为浮点数在某个区间内是无限的,没法缓存。
5. 实战避坑指南:那些让你想砸电脑的瞬间
5.1 循环中的“内存杀手”
如果你在循环里写这种代码,你的 CPU 会哭的:
Integer sum = 0;
for (int i = 0; i < 100000; i++) {
sum += i; // 这里每循环一次都会拆箱再装箱,产生 10 万个无用的 Integer 对象!
}
建议:在循环计算中,永远使用基本类型 int。
5.2 自动拆箱引发的 NullPointerException
这是最隐蔽的 Bug:
Integer i = null;
int n = i; // 抛出 NPE!因为底层调了 i.intValue(),null 调方法必死。
建议:在三元运算符或直接赋值时,一定要检查包装类是否为 null。
5.3 混合运算的诡异逻辑
当基本类型和包装类进行 == 比较时,包装类会自动拆箱。
Integer a = 128;
int b = 128;
System.out.println(a == b); // 结果:true!因为 a 拆箱成了 int 128,比的是值。
6. 大厂面试:关于装箱拆箱的连环炮
Q1:Integer i = new Integer(100) 和 Integer i = 100 有区别吗? 答案:有。前者直接在堆里 new 一个新对象,不走缓存;后者走 valueOf,可能命中缓存。
Q2:如何修改 Integer 缓存的上限? 答案:通过 JVM 参数 -XX:AutoBoxCacheMax=<size>。虽然没人会这么干,但面试官就爱听这个。
Q3:三元运算符里的装箱陷阱? 答案:如果三元运算符的两个结果类型不一致(比如一个 Integer 一个 Double),即使返回的是 Integer,也会因为“类型提升”发生自动拆箱,极易导致 NPE。
7. 结语
写完这篇日记,我终于把项目中那个“偶尔失效”的判断逻辑修好了——全改成用 equals() 比较对象。
作为大学生的我们:
-
永远记住:对象比较内容用
equals(),不要迷信==。 -
明白“自动”背后的成本,别让便捷成了性能的负担。
如果你也曾被 128 坑过,点个赞支持一下!下期咱们正式踏入 Java 集合框架
更多推荐

所有评论(0)