核心区别:基本类型 vs. 包装类
这是最根本的区别。

| 特性 | int |
Integer |
|---|---|---|
| 类型 | 基本数据类型 | 包装类 |
| 本质 | 它不是一个对象,是 Java 语言内置的一种数据类型,用于表示整数。 | 它是一个对象,是 int 基本类型的“包装器”。Integer 类是 java.lang 包中的一个 final 类。 |
| 存储位置 | 值直接存储在栈内存中。 | 对象存储在堆内存中,变量(引用)存储在栈内存中,指向堆中的对象。 |
| 默认值 | 0 |
null (非常重要!) |
| 功能 | 只能表示一个数值,不能调用方法。 | 拥有很多有用的静态方法和实例方法,toString(), parseInt(), compareTo() 等。 |
| 性能 | 占用内存小,计算速度快,直接在栈上操作。 | 作为对象,创建和销毁有额外的开销,内存占用较大。 |
Integer 的存在意义
既然已经有了 int,为什么还需要 Integer 呢?主要有以下几个原因:
a. 泛型支持
Java 的泛型(Generics)无法使用基本类型,你不能创建一个 List<int>,但可以创建一个 List<Integer>。
// 错误的用法 // List<int> numberList = new ArrayList<>(); // 正确的用法 List<Integer> numberList = new ArrayList<>(); numberList.add(10); // 自动装箱
b. 可以是 null
int 的默认值是 0,但 0 本身是一个有效的数值,在某些场景下,我们需要一个值来表示“不存在”或“未知”,这时,Integer 的 null 值就非常有用。
// 一个用户可能没有填写年龄 Integer userAge = null; // 表示年龄未知 // 而 int 无法表示这种“未知”状态 // int userAge = 0; // 0岁也是一个有效的年龄,无法区分“未知”和“0岁”
c. 提供丰富的工具方法
Integer 类封装了大量与整数操作相关的静态方法,非常方便。

// 字符串转整数 String str = "123"; int num = Integer.parseInt(str); // 获取整数的最大/最小值 int max = Integer.MAX_VALUE; // 2147483647 int min = Integer.MIN_VALUE; // -2147483648 // 转换为字符串 Integer myInt = 100; String strFromInt = myInt.toString();
自动装箱与拆箱
为了在 int 和 Integer 之间方便地转换,Java 5 引入了自动装箱和自动拆箱机制。
a. 自动装箱
当 int 类型的值需要被赋给一个 Integer 类型的变量时,JVM 会自动调用 Integer.valueOf(int) 方法,将其转换为 Integer 对象。
// 在 Java 5 之前,需要手动装箱 // Integer i = new Integer(10); // Java 5 之后,自动装箱 Integer i = 10; // JVM 内部执行了 Integer i = Integer.valueOf(10);
b. 自动拆箱
当 Integer 类型的值需要被赋给一个 int 类型的变量,或者参与算术运算时,JVM 会自动调用 Integer.intValue() 方法,将其拆箱为 int 值。
// 在 Java 5 之前,需要手动拆箱 // int primitive = i.intValue(); // Java 5 之后,自动拆箱 int primitive = i; // JVM 内部执行了 int primitive = i.intValue(); int sum = i + 5; // i 先被拆箱为 10,10 + 5 = 15
⚠️ 自动装箱/拆箱的陷阱
这个机制虽然方便,但也带来了一些需要特别注意的问题。

陷阱 1:NullPointerException
由于 Integer 对象可以是 null,如果尝试对一个 null 的 Integer 变量进行拆箱,就会抛出 NullPointerException。
Integer count = null; // 下面的代码会抛出 NullPointerException int intCount = count; // 尝试拆箱,但 count 是 null
陷阱 2:性能问题与对象开销
在一个循环中频繁地创建和销毁 Integer 对象会影响性能。
// 性能较差的写法
Long sum = 0L;
for (long i = 0; i < Integer.MAX_VALUE; i++) {
sum += i; // 每次循环都会创建一个新的 Long 对象
}
陷阱 3: 比较的陷阱 这是面试中非常经典的问题。 比较的是两个变量的内存地址(引用)。
- 对于
int: 比较的是值。 - 对于
Integer: 比较的是两个对象的地址是否相同。
Integer 的缓存机制(重点)
为了提高性能和减少内存占用,Integer 类在内部实现了一个 IntegerCache(缓存池),默认情况下,这个池缓存了 -128 到 127 之间的所有 Integer 对象。
Integer a = 127; Integer b = 127; System.out.println(a == b); // 输出 true,因为 a 和 b 指向缓存池中同一个对象 Integer c = 128; Integer d = 128; System.out.println(c == d); // 输出 false,因为 128 超出缓存范围,c 和 d 是两个不同的新对象 // 手动创建的 Integer 对象不会进入缓存池 Integer e = new Integer(127); Integer f = new Integer(127); System.out.println(e == f); // 输出 false,e 和 f 是两个通过 new 关键字创建的不同对象
比较 Integer 和 int
当 Integer 和 int 进行 比较时,Integer 会被自动拆箱为 int,然后比较两个 int 的值。
Integer g = 100; int h = 100; System.out.println(g == h); // 输出 true,g 被拆箱为 100,100 == 100 Integer i = new Integer(100); int j = 100; System.out.println(i == j); // 输出 true,i 被拆箱为 100,100 == 100
何时使用 int,何时使用 Integer?
| 场景 | 推荐类型 | 原因 |
|---|---|---|
| 方法参数/返回值 | 优先使用 int |
除非你需要用 null 表示特殊含义(如“未找到”、“未知”)。int 更高效,避免了 NullPointerException 的风险。 |
| 集合类(List, Set, Map)的键或值 | 必须使用 Integer |
集合只能存储对象,不能存储基本类型。 |
| 数据库字段映射 | 通常使用 Integer |
数据库的 NULL 值可以完美映射到 Java 的 Integer,而 int 无法处理 NULL,ORM 框架(如 MyBatis, JPA)通常会使用包装类。 |
| JSON/XML 序列化/反序列化 | 通常使用 Integer |
JSON/XML 中的 null 值可以映射到 Integer,而 int 会被强制赋值为 0,可能导致数据丢失。 |
int |
Integer |
|
|---|---|---|
| 一句话总结 | 原始的、快速的、不能为空的整数。 | 对象的、功能丰富的、可以为空的整数。 |
| 核心 | 基本类型,栈内存,值是 0。 |
引用类型,堆内存,值可以是 null。 |
| 转换 | 无需转换,直接使用。 | 通过自动装箱/拆箱与 int 交互。 |
| 选择 | 默认选择,当你只需要一个整数,且不需要 null 语义时。 |
特殊需求,当你需要 null 语义、使用泛型、或利用其工具方法时。 |
理解 int 和 Integer 的区别,是迈向 Java 中高级开发的关键一步,能帮助你写出更健壮、更符合 Java 语言习惯的代码。
