第5篇:装箱和拆箱

📌 系列导航《Java 100 天进阶之路》完整目录 |
⬅️ 上一篇:第4篇:秒懂数组拷贝,感知新境界 |
➡️ 下一篇:第6篇:Java异常有哪几种

一、核心知识点

  • 基本类型 vs 包装类(intIntegerdoubleDouble等)
  • 手动装箱/拆箱(new Integer(10) / intValue()
  • 自动装箱拆箱机制(Java 5引入)
  • 包装类的缓存池(IntegerCache,-128~127)
  • 性能陷阱与null风险

二、通俗讲解(1分钟开心学)

1. 为什么需要包装类?

基本类型(intdouble)效率高,但它们不是对象,无法放入集合(如ArrayList<int>是不允许的)。包装类(IntegerDouble)就是把基本类型“包”成对象,可以放进集合,也能调用方法(如Integer.parseInt)。

2. 装箱和拆箱是什么?

  • 装箱:把基本类型变成包装对象。像把一颗螺丝钉装进一个工具箱里。
  • 拆箱:从包装对象里取出基本类型。像打开工具箱拿出螺丝钉。

Java 5之前,你必须手动装箱:

Integer i = new Integer(10);   // 手动装箱
int j = i.intValue();          // 手动拆箱

Java 5以后,编译器自动帮你装箱和拆箱:

Integer i = 10;   // 自动装箱(编译成 Integer.valueOf(10))
int j = i;        // 自动拆箱(编译成 i.intValue())

生活类比
自动装箱就像超市自助结账——你把商品(基本类型)放在扫描区,机器自动把它“包装”成小票(包装类);拆箱就像你把小票上的商品价格取出来,机器自动帮你计算。

3. 缓存池陷阱(面试常考)

Integer内部有一个缓存池,默认缓存了-128~127之间的整数。当你使用自动装箱时,如果数值在这个范围内,会直接返回缓存的对象;如果超出范围,会创建新对象。

Integer a = 127;  // 从缓存取
Integer b = 127;  // 从缓存取
System.out.println(a == b); // true(同一个对象)

Integer c = 128;  // new Integer(128)
Integer d = 128;  // new Integer(128)
System.out.println(c == d); // false(不同对象)

记住:比较包装类的内容,永远用equals(),不要用==

三、实操代码案例 + 场景说明

场景:你需要在ArrayList中存储数字,并计算总和。

import java.util.ArrayList;

public class BoxingDemo {
    public static void main(String[] args) {
        ArrayList<Integer> scores = new ArrayList<>();
        // 自动装箱:int -> Integer
        scores.add(95);
        scores.add(87);
        scores.add(100);
        
        int sum = 0;
        for (Integer score : scores) {
            // 自动拆箱:Integer -> int
            sum += score;
        }
        System.out.println("总分:" + sum);
    }
}

使用==比较包装类的危险示范

Integer x = 100;
Integer y = 100;
System.out.println(x == y);   // true(缓存内)
Integer m = 200;
Integer n = 200;
System.out.println(m == n);   // false(缓存外)
// 正确做法
System.out.println(m.equals(n)); // true

四、避坑要点

错误/误区 后果 正确做法
在循环中用包装类做累加 每次循环都装箱拆箱,创建大量临时对象,性能差 循环内使用基本类型累加,最后再装箱
null的包装类进行自动拆箱 NullPointerException 先判空,或使用Optional包装
==比较包装类 缓存范围外的值会误判 始终使用equals()比较内容
以为Integer i = null; int j = i;会优雅失败 程序崩溃 避免不安全的拆箱,或显式判断

性能反面示例(不要这样写):

Integer sum = 0;
for (int i = 0; i < 10000; i++) {
    sum += i;   // 每次循环装箱+拆箱
}
// 应该用 int sum = 0;

五、面试高频考点

Q1:什么是自动装箱和拆箱?

编译器自动将基本类型转换为包装类(调用valueOf())称为装箱,反向(调用xxxValue())称为拆箱。从Java 5开始支持。

Q2:Integer a = 127; Integer b = 127; 为什么a==btrue

Integer.valueOf() 方法对-128~127范围内的值使用了缓存,多次调用返回同一个实例。超出范围则new新对象。

Q3:下列代码输出什么?

Integer i1 = new Integer(10);
Integer i2 = 10;
System.out.println(i1 == i2);

falsenew强制创建新对象,而自动装箱10在缓存范围内,但缓存的对象与new的对象不是同一个。

六、练习题

  1. 简答:解释Integer i = 128; int j = 128; System.out.println(i == j); 输出什么?为什么(提示:拆箱后比较值)?
  2. 代码优化:以下代码有什么性能问题?如何修改?
    Long sum = 0L;
    for (long i = 0; i < Integer.MAX_VALUE; i++) {
        sum += i;
    }
    
  3. 动手:写一个小程序,通过-Djava.lang.Integer.IntegerCache.high=1000 JVM参数,将Integer缓存上限扩大到1000,验证200是否命中缓存。

📊 你的学习进度

  • 当前:第5篇 / 共44篇 · 第二阶段:核心语法与面向对象(第5~20篇)
  • ✅ 已完成:第1~4篇
  • 📖 正在学:第5篇
  • ⏳ 待学习:第6~44篇

👉 📚 完整目录 & 学习指南 | 🔥 订阅本专栏,不错过每一篇

💡 本专栏每篇都包含:避坑表 + 面试高频考点 + 练习题。每天30分钟,100天拿offer!


👉 下一篇文章预告

《Java异常有哪几种》

内容简介:检查型异常、运行时异常、Error 有什么区别?什么时候该用 try-catch,什么时候该改代码?

💡 学完这篇,你将彻底分清异常体系,写出更健壮的代码。

📌 《Java 100 天进阶之路 | 从入门到上岗就业》 每天一篇,建议收藏 + 关注,一起100天拿offer!
👉 点击关注我,更新后第一时间收到推送!

更多推荐