基本类型与包装类:8 种基本类型、自动装箱拆箱与缓存
更新时间:2026-09-20。本篇讲清 Java 类型系统的第一道分水岭:基本类型存值、包装类存对象。这也是理解「栈上/堆上」「== 为什么有时成立有时不成立」的起点,承接 JVM 运行时内存与堆分代。
一、8 种基本类型速查
| 分类 | 类型 | 字节 | 默认值 | 取值范围/说明 |
|---|---|---|---|---|
| 整型 | byte | 1 | 0 | -128~127 |
| 整型 | short | 2 | 0 | ±3.2 万 |
| 整型 | int | 4 | 0 | ±21 亿,整数字面量默认类型 |
| 整型 | long | 8 | 0L | 字面量要加 L |
| 浮点 | float | 4 | 0.0f | 字面量加 f,约 6~7 位有效数字 |
| 浮点 | double | 8 | 0.0d | 默认浮点类型,约 15 位 |
| 字符 | char | 2 | '\u0000' | UTF-16 代码单元,0~65535 |
| 布尔 | boolean | — | false | JVM 中无独立字节码指令 |
记忆点:除了这 8 种,其他一切都是引用类型(包括数组、String、枚举、你自己写的类)。
二、值与对象:存储位置不同
java
int n = 100; // 基本类型:栈帧局部变量表里直接存值
Integer m = 100; // 引用类型:栈上放引用,对象在堆上
int[] arr = new int[3]; // 数组本身是对象(在堆),元素是 int 值| 维度 | 基本类型 int | 包装类 Integer |
|---|---|---|
| 存储 | 栈上的值 | 堆对象 + 栈引用 |
| 默认值 | 0 | null(可表示「没有值」) |
| 能否为 null | 否 | 能 |
| 能否用于泛型 | 否(List<int> 非法) | 能(List<Integer>) |
| 比较 | == 比值 | == 比引用,equals 比值 |
三、自动装箱与拆箱
编译器在需要对象的地方把 int 包成 Integer(装箱),在需要值的地方拆回(拆箱):
java
Integer a = 5; // 等价 Integer.valueOf(5) 自动装箱
int b = a; // 等价 a.intValue() 自动拆箱
Integer c = a + 3; // 先 a.intValue() 拆箱相加,再 valueOf 装箱
四、Integer 缓存:== 最经典的坑
Integer.valueOf(int) 会缓存 -128 ~ 127(上限可由 JVM 参数调整)之间的对象:
java
Integer x = 127, y = 127;
System.out.println(x == y); // true,命中缓存,同一个对象
Integer p = 128, q = 128;
System.out.println(p == q); // false,超出范围,各自 new
System.out.println(p.equals(q)); // true,比值才可靠规则:两个包装类比较,永远用 equals(或先拆箱再比),不要赌缓存。 缓存只在「自动装箱走 valueOf」时生效;显式 new Integer(1)(已废弃)每次都是新对象。
坑
- 包装类为 null 时自动拆箱抛 NPE:
Integer z=null; int v=z;等价z.intValue(),直接空指针。Mapget出 null 再赋给基本类型是高频事故。 - 循环里反复装箱产生垃圾:
Long sum=0L; for(...) sum+=i;每次都 new 新对象,百万次循环制造短命对象、加重 Minor GC,循环累加用基本类型。 - 三目运算符类型对齐:
flag ? 1 : integerNull中 1 被装箱成 Integer,另一侧为 null,拆箱即 NPE。 ==混比一个基本一个包装:此时包装类会拆箱比值,反而成立,极易让上面「128 用 == 失败」的判断更迷惑——统一用 equals 最省心。
总结
Java 有且只有 8 种基本类型在栈上直接存值、默认 0/false,其余都是堆对象;包装类让值能为 null、能进泛型集合,编译器通过 valueOf/xxxValue 自动装箱拆箱,但 Integer 对 -128~127 做了对象缓存,所以包装类用 == 比较结果随数值而变——比较一律用 equals、循环累加用基本类型、并对可能为 null 的包装类拆箱保持警惕。 相关的对象内存与回收见 JVM 堆分代。