【Java八股|第10篇】Java 中的包装类和自动拆装箱

前言

这一篇我们继续学习 Java 基础中非常高频的面试题:包装类和自动拆装箱。

在 Java 中,有基本数据类型,比如:

text 复制代码
int
long
double
boolean
char

也有对应的包装类,比如:

text 复制代码
Integer
Long
Double
Boolean
Character

刚开始学习时,很多人会觉得它们差不多。比如:

java 复制代码
int a = 10;
Integer b = 10;

看起来都是保存数字 10。

但是在面试和实际开发中,这里面有很多细节:

  • 基本类型和包装类有什么区别?
  • 什么是自动装箱和自动拆箱?
  • Integer a = 127; Integer b = 127; 为什么是 true?
  • Integer a = 128; Integer b = 128; 为什么是 false?
  • 包装类能不能为 null?
  • 为什么数据库实体类更常用包装类?

这一篇我们就把包装类和自动拆装箱系统梳理一下。

一、基本数据类型和包装类

Java 有 8 种基本数据类型:

基本类型 包装类
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean

基本类型不是对象,包装类是对象。

比如:

java 复制代码
int a = 10;
Integer b = 10;

a 是基本类型变量,直接保存数值。

b 是引用类型变量,保存的是对象引用。

二、为什么需要包装类?

既然有基本类型,为什么还要有包装类?

主要原因有几个。

1. 集合不能直接存基本类型

Java 集合中不能直接存基本数据类型。

比如:

java 复制代码
List<int> list = new ArrayList<>();

这是错误写法。

应该写成:

java 复制代码
List<Integer> list = new ArrayList<>();

因为泛型要求使用引用类型,不能使用基本类型。

2. 包装类可以表示 null

基本类型不能为 null。

java 复制代码
int age = null;

这是错误的。

但是包装类可以:

java 复制代码
Integer age = null;

在项目中,null 经常表示"没有值"或"未填写"。

比如用户年龄没有填写,用 Integer 就更合适。

3. 包装类提供很多工具方法

比如字符串转数字:

java 复制代码
Integer num = Integer.valueOf("123");

或者:

java 复制代码
int num = Integer.parseInt("123");

这些方法都在包装类中。

三、什么是自动装箱?

自动装箱就是:

基本类型自动转换成对应的包装类。

示例:

java 复制代码
Integer num = 10;

这里右边是基本类型 int,左边是包装类 Integer。

编译器会自动帮我们转换,大致相当于:

java 复制代码
Integer num = Integer.valueOf(10);

这就是自动装箱。

再看集合中的例子:

java 复制代码
List<Integer> list = new ArrayList<>();

list.add(10);

list 中存的是 Integer,但我们传的是 int。

编译器会自动装箱成:

java 复制代码
list.add(Integer.valueOf(10));

四、什么是自动拆箱?

自动拆箱就是:

包装类自动转换成对应的基本类型。

示例:

java 复制代码
Integer num = 10;

int value = num;

这里 num 是 Integer,value 是 int。

编译器会自动转换,大致相当于:

java 复制代码
int value = num.intValue();

再看一个例子:

java 复制代码
Integer a = 10;
Integer b = 20;

int result = a + b;

这里 a + b 时,Integer 会自动拆箱成 int,然后进行加法运算。

五、自动拆箱的空指针问题

自动拆箱最容易出现的问题是空指针异常。

看下面代码:

java 复制代码
Integer num = null;

int value = num;

这段代码会报:

text 复制代码
NullPointerException

为什么?

因为自动拆箱时底层会调用:

java 复制代码
num.intValue()

但是 num 是 null,所以调用方法就会空指针。

再看一个项目中常见例子:

java 复制代码
public void updateStatus(Integer status) {
    if (status == 1) {
        System.out.println("启用");
    }
}

如果调用时传入:

java 复制代码
updateStatus(null);

这里:

java 复制代码
status == 1

会触发自动拆箱,相当于:

java 复制代码
status.intValue() == 1

如果 status 是 null,就会空指针。

更安全的写法:

java 复制代码
if (Integer.valueOf(1).equals(status)) {
    System.out.println("启用");
}

或者:

java 复制代码
if (status != null && status == 1) {
    System.out.println("启用");
}

六、Integer 缓存机制

这是包装类中最经典的面试点。

看下面代码:

java 复制代码
Integer a = 127;
Integer b = 127;

System.out.println(a == b);

输出:

text 复制代码
true

再看:

java 复制代码
Integer a = 128;
Integer b = 128;

System.out.println(a == b);

输出:

text 复制代码
false

为什么?

因为 Integer 有缓存机制。

默认情况下,Integer 会缓存:

text 复制代码
-128 到 127

之间的整数对象。

当写:

java 复制代码
Integer a = 127;

底层相当于:

java 复制代码
Integer a = Integer.valueOf(127);

对于 -128 到 127 之间的数字,Integer.valueOf() 会从缓存中取对象。

所以:

java 复制代码
Integer a = 127;
Integer b = 127;

a 和 b 指向同一个缓存对象,a == b 为 true。

但是:

java 复制代码
Integer a = 128;
Integer b = 128;

128 超出了默认缓存范围,会创建不同对象,所以 a == b 为 false。

七、Integer 比较应该用什么?

包装类比较内容时,建议使用:

java 复制代码
equals

比如:

java 复制代码
Integer a = 128;
Integer b = 128;

System.out.println(a.equals(b));

输出:

text 复制代码
true

不要用:

java 复制代码
a == b

因为 == 比较的是对象地址。

对于包装类,尤其是 Integer、Long,用 == 很容易因为缓存范围导致结果不一致。

更安全写法:

java 复制代码
Objects.equals(a, b)

示例:

java 复制代码
Integer a = null;
Integer b = 10;

System.out.println(Objects.equals(a, b));

Objects.equals 可以避免空指针问题。

八、new Integer 和 valueOf 的区别

虽然现在不推荐使用 new Integer(),但面试可能会问。

java 复制代码
Integer a = new Integer(127);
Integer b = new Integer(127);

System.out.println(a == b);

输出:

text 复制代码
false

因为 new Integer(127) 每次都会创建新对象,不会使用缓存。

而:

java 复制代码
Integer a = Integer.valueOf(127);
Integer b = Integer.valueOf(127);

System.out.println(a == b);

输出:

text 复制代码
true

因为 valueOf 会使用缓存。

所以实际开发中不应该手动 new Integer(),而应该使用自动装箱或 Integer.valueOf()。

九、parseInt 和 valueOf 的区别

java 复制代码
int a = Integer.parseInt("123");
Integer b = Integer.valueOf("123");

区别是:

方法 返回值
parseInt int 基本类型
valueOf Integer 包装类

示例:

java 复制代码
int a = Integer.parseInt("123");
Integer b = Integer.valueOf("123");

如果你需要基本类型,用 parseInt。

如果你需要包装类,用 valueOf。

十、实体类中为什么常用包装类?

在 Spring Boot + MyBatis 项目中,实体类经常这样写:

java 复制代码
public class Emp {
    private Integer id;
    private String name;
    private Integer gender;
    private LocalDate entryDate;
}

为什么不用:

java 复制代码
private int id;
private int gender;

原因是包装类可以表示 null。

比如新增员工时,前端可能没有传 id,因为 id 由数据库自增生成。

如果用 int,默认值是:

text 复制代码
0

这会让人分不清:

text 复制代码
id 是真的为 0
还是根本没传

而 Integer 可以是:

text 复制代码
null

表示没有值。

所以实体类、DTO、VO 中经常使用包装类。

十一、包装类和数据库字段的关系

数据库中字段可能为空:

sql 复制代码
gender tinyint unsigned null

如果 Java 中用基本类型:

java 复制代码
private int gender;

当数据库值是 null 时,映射可能会出问题,或者默认变成 0。

但 0 不一定是合法业务值。

更推荐:

java 复制代码
private Integer gender;

这样数据库中的 null 可以准确映射成 Java 中的 null。

所以项目开发中:

数据库字段如果允许为空,Java 实体类通常使用包装类。

十二、面试标准回答

如果面试官问:

基本数据类型和包装类有什么区别?什么是自动拆装箱?

可以这样回答:

基本数据类型不是对象,直接保存具体值;包装类是对象,是基本类型对应的引用类型,比如 int 对应 Integer,long 对应 Long。

自动装箱是基本类型自动转换成包装类,比如 Integer num = 10,底层相当于 Integer.valueOf(10)。自动拆箱是包装类自动转换成基本类型,比如 int value = num,底层相当于调用 num.intValue()。

使用包装类时要注意空指针问题。如果包装类对象是 null,自动拆箱时会调用对应方法,比如 intValue(),就会抛出 NullPointerException。

另外,Integer 有缓存机制,默认缓存 -128 到 127。所以 Integer a = 127; Integer b = 127; 用 == 比较是 true,但 128 可能是 false。包装类比较值时,建议使用 equals 或 Objects.equals。

十三、常见问题总结

1. Integer a = 127; Integer b = 127; a == b 是什么?

结果是:

text 复制代码
true

因为 127 在 Integer 默认缓存范围内。

2. Integer a = 128; Integer b = 128; a == b 是什么?

结果通常是:

text 复制代码
false

因为 128 超出默认缓存范围,会创建不同对象。

3. 包装类比较能不能用 ==?

不推荐。

包装类是对象,== 比较地址。

比较值建议使用:

java 复制代码
Objects.equals(a, b)

4. 自动拆箱有什么风险?

如果包装类是 null,自动拆箱会抛出空指针异常。

比如:

java 复制代码
Integer num = null;
int value = num;

5. 实体类字段用基本类型还是包装类?

通常建议使用包装类。

因为包装类可以表示 null,更适合和数据库字段、前端参数对应。

十四、实际开发建议

实际开发中可以注意下面几点:

  1. 集合泛型使用包装类,不能使用基本类型
  2. 实体类、DTO、VO 中数字字段优先使用包装类
  3. 包装类比较值时使用 Objects.equals
  4. 不要依赖 Integer 缓存范围写业务逻辑
  5. 注意自动拆箱导致的空指针异常
  6. 金额不要用 Double,优先使用 BigDecimal
  7. 字符串转数字时注意捕获 NumberFormatException
  8. 不推荐手动使用 new Integer()

十五、总结

这一篇主要学习了 Java 中的包装类和自动拆装箱。

基本数据类型不是对象,包装类是对象。Java 为 8 种基本类型都提供了对应包装类,比如 int 对应 Integer,long 对应 Long。

自动装箱就是基本类型自动转包装类,自动拆箱就是包装类自动转基本类型。它们让代码写起来更方便,但也带来了空指针风险。

Integer 默认缓存 -128 到 127,所以包装类用 == 比较时可能出现看起来奇怪的结果。实际开发中比较包装类值,建议使用 Objects.equals。

在 Java 后端项目中,实体类字段经常使用包装类,因为包装类可以表示 null,更适合接收前端参数和映射数据库字段。

相关推荐
驭渊的小故事3 小时前
MyBatis XML 开发实战:从配置到 CRUD 全解析
xml·java·mybatis
哦虎!8 小时前
【JavaEE】网络初识
java
for_ever_love__8 小时前
MySQL ORDER BY 排序优化:从 Using filesort 到索引有序扫描(含双路 / 单路排序原理)
java·mysql·优化·存储·by·order·filesort
我是人✓9 小时前
Java文件上传三方式对比:从传统到SpringBoot
java·开发语言
欣栀♡9 小时前
JVM 频繁 Full GC 先看日志括号里的 cause,jstat 的 FGC 在 JDK 8 上会骗人
java·jvm
IT研究室9 小时前
最新计算机毕业设计选题推荐-基于spring boot的葡萄酒文化宣传网站-网站-文档指导-Java-springboot
java·spring boot·课程设计
慧都小项9 小时前
MyEclipse 中创建和测试 JAX-RS REST 接口:从项目配置到 REST Explorer
java·restful·myeclipse·jax-rs
Wang's Blog9 小时前
Java 项目实战: 交友项目 - 入门案例:编写 Dubbo 服务提供者
java·dubbo·交友
秋饼10 小时前
Spring AI 2.0 结构化输出企业级实战:从 BeanOutputConverter 源码到生产级解析容错
java·ai·技术分享·后端开发
摇滚侠10 小时前
《计算机是如何工作的:人人都能懂的计算机软硬件工作原理》第 9 章 机器码与汇编语言 阅读笔记 9
java·开发语言·笔记