包装类与自动装拆箱

8种基本类型对应的包装类

Java有两套类型系统:基本类型(放在栈上,快)和引用类型(放在堆上,功能多)

包装类就是给基本类型穿了件衣服,让它们变成对象,能参加”对象世界”的活动

基本类型 包装类 内存大小 默认值
byte Byte 1字节 0
short Short 2字节 0
int Integer 4字节 0
long Long 8字节 0L
float Float 4字节 0.0f
double Double 8字节 0.0
char Character 2字节 ‘\u0000’
boolean Boolean 不确定 false

注意int 对应 Integer(不是Int),char 对应 Character(不是Char),这两个名字不一样

为什么需要包装类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Test
public void testWhyWrapper() {
// 原因1:泛型只能用引用类型
// List<int> list = new ArrayList<>(); // ❌ 编译报错!
List<Integer> list = new ArrayList<>(); // ✅ 必须用Integer

// 原因2:集合不能放基本类型
list.add(1); // 自动装箱:int → Integer
list.add(2);
list.add(3);

// 原因3:需要null值来表示"没有值"
Integer score = null; // 表示成绩还没录入
// int score2 = null; // ❌ 基本类型不能是null

// 原因4:包装类提供了很多实用方法
int num = Integer.parseInt("123"); // 字符串转int
String str = Integer.toString(456); // int转字符串
int max = Integer.MAX_VALUE; // int最大值:2147483647
String binary = Integer.toBinaryString(10); // 转二进制:"1010"
System.out.println("parseInt: " + num);
System.out.println("MAX_VALUE: " + max);
System.out.println("toBinaryString: " + binary);
}

自动装箱和自动拆箱

装箱就是基本类型穿上对象的外衣,拆箱就是脱掉外衣变回基本类型

Java 5 以后自动帮你做,不用手动转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Test
public void testAutoBoxing() {
// 自动装箱:int → Integer(编译器自动调用 Integer.valueOf())
Integer a = 100; // 等价于 Integer a = Integer.valueOf(100);

// 自动拆箱:Integer → int(编译器自动调用 .intValue())
int b = a; // 等价于 int b = a.intValue();

// 运算时也会自动拆箱
Integer x = 10;
Integer y = 20;
int sum = x + y; // x和y先拆箱成int,再相加
System.out.println("sum = " + sum); // 30

// 赋值也可以
Double d = 3.14; // 自动装箱
double dd = d; // 自动拆箱

// 方法参数也会自动转换
List<Integer> list = new ArrayList<>();
list.add(42); // 自动装箱:42 → Integer.valueOf(42)
int val = list.get(0); // 自动拆箱:Integer → int
}

⚠️ 陷阱1:Integer缓存

这是面试超高频考点,必须搞懂

Java对 -128~127 范围内的Integer做了缓存,这个范围内 == 比较是true,超出范围就是false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Test
public void testIntegerCache() {
// -128~127 范围内:valueOf() 返回缓存的对象
Integer a = 127; // Integer.valueOf(127),从缓存取
Integer b = 127; // Integer.valueOf(127),从缓存取,同一个对象
System.out.println(a == b); // true!同一个对象
System.out.println(a.equals(b)); // true

// 超出范围:valueOf() 每次都new新对象
Integer c = 128; // Integer.valueOf(128),new了一个新对象
Integer d = 128; // Integer.valueOf(128),又new了一个新对象
System.out.println(c == d); // false!两个不同的对象
System.out.println(c.equals(d)); // true!值一样

// 再看个-128的
Integer e = -128;
Integer f = -128;
System.out.println(e == f); // true!在缓存范围内

Integer g = -129;
Integer h = -129;
System.out.println(g == h); // false!超出缓存范围
}

原理Integer.valueOf() 内部有个缓存数组,-128到127的Integer对象预先创建好了

铁律:包装类比较永远用 .equals(),别用 ==

参考 运算符 中关系运算符和 String类与字符串操作 中的 == vs equals

⚠️ 陷阱2:拆箱遇到null

当一个包装类对象是null,自动拆箱时会直接炸掉(NullPointerException)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Test
public void testNullUnboxing() {
// 场景1:直接赋值
Integer num = null;
// int n = num; // ❌ NPE!null.intValue() 炸了

// 场景2:方法返回null
// public Integer getScore() { return null; }
// int score = getScore(); // ❌ NPE!

// 场景3:集合取出null
List<Integer> list = new ArrayList<>();
list.add(null);
// int val = list.get(0); // ❌ NPE!

// 场景4:三元运算符的坑
boolean flag = true;
Integer i = null;
// int result = flag ? i : 0; // ❌ NPE!因为返回类型是int,i被强制拆箱

// ✅ 正确做法:用之前先判null
Integer score = null;
int safeScore = (score != null) ? score : 0;
System.out.println("安全的分数:" + safeScore); // 0
}

防御建议

从数据库、接口拿到的包装类,用之前先判null

或者用 Optional(Java 8+)来处理

常用转换方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Test
public void testConversion() {
// String → int
int a = Integer.parseInt("123");
System.out.println(a + 1); // 124

// String → double
double b = Double.parseDouble("3.14");
System.out.println(b); // 3.14

// int → String(三种方式)
String s1 = String.valueOf(42);
String s2 = Integer.toString(42);
String s3 = 42 + ""; // 偷懒写法,但性能稍差
System.out.println(s1); // "42"

// ⚠️ parseInt遇到非数字会炸
try {
int bad = Integer.parseInt("abc"); // NumberFormatException
} catch (NumberFormatException e) {
System.out.println("不是数字:" + e.getMessage());
}

// ⚠️ parseInt遇到小数也会炸
try {
int bad = Integer.parseInt("3.14"); // NumberFormatException
} catch (NumberFormatException e) {
System.out.println("整数解析不了小数:" + e.getMessage());
}
}
转换方向 方法 示例
String → int Integer.parseInt("123") 123
String → double Double.parseDouble("3.14") 3.14
String → boolean Boolean.parseBoolean("true") true
int → String String.valueOf(42) “42”
int → String Integer.toString(42) “42”
int → Integer Integer.valueOf(42) Integer对象

常见坑总结

说明 怎么避免
== 比较Integer -128~127是true,其他是false 永远用 .equals()
null拆箱NPE Integer num = null; int n = num; 会炸 用之前先判null
parseInt非数字 Integer.parseInt("abc") 抛异常 try-catch或先校验
性能浪费 频繁装箱拆箱有开销 能用基本类型就别用包装类
三元运算符拆箱 flag ? integerObj : 0 可能NPE 保持两边类型一致

练习题

题1:预测输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void testPredictOutput() {
Integer a = 100;
Integer b = 100;
Integer c = 200;
Integer d = 200;

System.out.println(a == b); // ?
System.out.println(c == d); // ?
System.out.println(a.equals(b)); // ?
System.out.println(c.equals(d)); // ?
}
// 答案:true, false, true, true
// 100在缓存范围内所以==是true,200不在所以==是false
// equals比较值,都是true

题2:找出下面代码的Bug

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void testFindBug() {
// Bug代码:
Map<String, Integer> map = new HashMap<>();
map.put("score", null);
// int score = map.get("score"); // ❌ NPE!null自动拆箱

// 修复:
Integer score = map.get("score");
int safeScore = (score != null) ? score : 0;
System.out.println("分数:" + safeScore); // 0
}

题3:比较以下两种写法的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
public void testCompare() {
// 写法1:用基本类型
long start1 = System.currentTimeMillis();
long sum1 = 0L;
for (int i = 0; i < 10000000; i++) {
sum1 += i;
}
System.out.println("基本类型耗时:" + (System.currentTimeMillis() - start1) + "ms");

// 写法2:用包装类(注意这里用了Long)
long start2 = System.currentTimeMillis();
Long sum2 = 0L; // ← 这里用了Long包装类
for (int i = 0; i < 10000000; i++) {
sum2 += i; // 每次循环都在装箱拆箱!
}
System.out.println("包装类耗时:" + (System.currentTimeMillis() - start2) + "ms");
// 包装类版本明显更慢,因为每次 += 都要拆箱→运算→装箱
}

上一章 目录 下一章
StringBuilder与StringBuffer java基础 Object类