
概述
Java 引入包装类的主要原因有三:
1、面向对象的需求
Java 的基本数据类型(如 int、double)不是对象,而 Java 的很多框架、集合(如 ArrayList、HashMap)只能存放对象。因此需要“包装”基本类型,使其能作为对象使用。
2、提供更多功能
包装类为基本类型提供了丰富的方法(如转换、比较、解析字符串等)。例如:
Integer.parseInt("123");
Character.isDigit('9');
3、支持泛型与集合框架
泛型不支持基本类型,因此必须使用包装类。例如:
List<Integer> list = new ArrayList<>(); // 不能使用 List<int>
包装类(Wrapper Class)是 Java 为每个基本数据类型提供的对应的引用类型(类)。
- 将基本类型“封装”为对象;
- 提供了操作基本类型的各种方法;
- 支持自动拆箱/装箱(Java 5 引入)。
有哪些包装类
Java 为每一种基本数据类型都提供了对应的包装类:
| 基本类型 | 包装类 |
|---|---|
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
| char | Character |
| boolean | Boolean |
其中,除了 Character 和 Boolean,父类都是 Number
封装以后的,内存结构对比:
public static void main(String[] args){
int num = 520; // 栈
Integer obj = new Integer(520); // 栈中是 obj 指针,指向堆中存放value的地址
}
装箱与拆箱
装箱:把基本类型转换成包装类对象。
✔ 旧方式(Java 5 之前,手动装箱)
int a = 10;
Integer integerObj = Integer.valueOf(a); // 装箱
拆箱:把包装类对象转换为基本类型。
✔ 旧方式(Java 5 之前,手动拆箱)
Integer integerObj = Integer.valueOf(10);
int b = integerObj.intValue(); // 拆箱 xxxValue()
自动装箱与拆箱:从 Java 5 开始,编译器自动进行装箱和拆箱,无需手动调用 xxxValue() 或 valueOf()。
int a = 10;
Integer integerObj = a; // 自动装箱,相当于 Integer.valueOf(a)
Integer integerObj = 20;
int b = integerObj; // 自动拆箱,相当于 integerObj.intValue()
Integer i = 4; // 自动装箱,相当于 Integer i = Integer.valueOf(4);
i = i + 5; // 等号右边:将i对象转成基本数值(自动拆箱) i.intValue() + 5;
// 加法运算完成后,再次装箱,把基本数值转成对象。
Integer x = 5;
Integer y = 10;
int sum = x + y; // x 和 y 自动拆箱为 int
集合不能存基本类型,只能存对象:
List<Integer> list = new ArrayList<>();
list.add(100); // 自动装箱为 new Integer(100)
int value = list.get(0); // 自动拆箱为 int
注意:只能与自己对应的类型之间才能实现自动装箱与拆箱。
Integer i = 1;
Double d = 1; // 错误的,1是int类型
自定义包装类
Java 语言中已经提供了 8 个基本数据类型的包装类。但在某些业务场景下,你可能需要为特定数据结构或行为创建自己的包装对象,这就是 自定义包装类。
简单来说:
👉 自定义包装类 = 对某个数据或对象进行“封装”,让它具有更丰富的行为或更安全的操作方式。
📌 场景 1:需要对数据进行额外校验
例如包裹一个年龄字段,要求只能在 0–150 之间。
📌 场景 2:需要不可变对象(Immutable)
像 Java 的 String 和包装类都是不可变的,你可以自己创建不可变数据。
📌 场景 3:需要实现某些自定义逻辑
例如:
- 限制值域
- 自动格式化数据
- 在赋值时触发事件
📌 场景 4:提高可读性与领域建模能力(DDD)
如将 int price 替换为 Money 类型,更能表达语义。
下面示例创建一个包装整数的类 MyInteger:
public class MyInteger {
private int value;
public MyInteger(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
MyInteger num = new MyInteger(10);
System.out.println(num.getValue()); // 输出:10
加入校验(增强包装类的价值)
例如:年龄 Age 必须在 0–150 之间。
public class Age {
private final int value;
public Age(int value) {
if (value < 0 || value > 150) {
throw new IllegalArgumentException("Age must be between 0 and 150.");
}
this.value = value;
}
public int getValue() {
return value;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
Age age = new Age(25);
// Age age = new Age(200); // 会报错
⚠️注意事项
✔ 一般建议包装类设计为不可变(Immutable)
将字段设为 final,避免并发问题。
✔ 重写 equals() 和 hashCode()
如果希望对象能放入 HashMap / HashSet,需要:
@Override
public boolean equals(Object o) { ... }
@Override
public int hashCode() { ... }
✔ 考虑与原始类型的转换
例如:
public int toInt() { return value; }
✔ 考虑自动装箱功能
Java 的自动装箱无法应用于自定义类型,使用时需手动转换。
常用 API
最大值与最小值
所有数值型包装类都包含 MAX_VALUE 和 MIN_VALUE 常量。
public class WrapperDemo1 {
public static void main(String[] args) {
System.out.println("Integer 最大值: " + Integer.MAX_VALUE);
System.out.println("Integer 最小值: " + Integer.MIN_VALUE);
System.out.println("Double 最大值: " + Double.MAX_VALUE);
System.out.println("Double 最小值: " + Double.MIN_VALUE);
System.out.println("Short 最大值: " + Short.MAX_VALUE);
System.out.println("Short 最小值: " + Short.MIN_VALUE);
}
}
字符大小写转换
Character 提供字符处理的静态方法:
| 方法 | 说明 |
|---|---|
isUpperCase(char) | 判断是否大写 |
isLowerCase(char) | 判断是否小写 |
toUpperCase(char) | 转大写 |
toLowerCase(char) | 转小写 |
public class WrapperDemo2 {
public static void main(String[] args) {
char c1 = 'a';
char c2 = 'Z';
System.out.println(Character.isLowerCase(c1)); // true
System.out.println(Character.isUpperCase(c2)); // true
System.out.println(Character.toUpperCase(c1)); // A
System.out.println(Character.toLowerCase(c2)); // z
}
}
整数转不同进制
Integer 提供了三个常用静态方法:
| 方法 | 说明 |
|---|---|
toBinaryString(int) | 转二进制 |
toOctalString(int) | 转八进制 |
toHexString(int) | 转十六进制 |
public class WrapperDemo3 {
public static void main(String[] args) {
int num = 100;
System.out.println(Integer.toBinaryString(num)); // 1100100
System.out.println(Integer.toOctalString(num)); // 144
System.out.println(Integer.toHexString(num)); // 64
}
}
字符串与基本类型互转
字符串 → 基本类型
使用 parseXxx() 方法:
int a = Integer.parseInt("123");
double b = Double.parseDouble("3.14");
boolean c = Boolean.parseBoolean("true");
基本类型 → 字符串
使用 String.valueOf() 或包装类的 toString():
String s1 = String.valueOf(123);
String s2 = Integer.toString(456);
比较
| 方法 | 说明 |
|---|---|
equals(Object) | 比较值是否相等 |
compareTo(T another) | 比较大小(返回正/负/0) |
public class WrapperDemo4 {
public static void main(String[] args) {
Integer x = 20;
Integer y = 15;
// 比较值是否相等
System.out.println(x.equals(y)); // false
// 比较大小
System.out.println(x.compareTo(y)); // 1(大于)
System.out.println(y.compareTo(x)); // -1(小于)
System.out.println(x.compareTo(20)); // 0(相等)
}
}
包装类对象特性
✨包装类缓存对象
| 包装类 | 缓存对象 |
|---|---|
| Byte | -128~127 |
| Short | -128~127 |
| Integer | -128~127 |
| Long | -128~127 |
| Float | 没有 |
| Double | 没有 |
| Character | 0~127 |
| Boolean | true 和 false |
举例
Integer a = 1;
Integer b = 1;
System.out.println(a == b); // true
Java中,`Integer`有一个缓存池,它会缓存`-128`到`127`之间的所有值。当你创建值在这个范围内的`Integer`对象时,Java会返回缓存中的对象,而不是创建一个新的对象。所以当`a`和`b`都赋值为`1`时,它们引用的是同一个对象。因此`a == b`为`true`,因为`==`比较的是对象的引用是否相同。
Integer i = 128;
Integer j = 128;
System.out.println(i == j); // false
Java中,Integer对象的缓存池只缓存-128到127之间的值。128超出了这个范围,因此i和j是两个不同的对象。即使它们的值相同,但它们指向不同的内存位置。所以i == j返回false,因为==比较的是引用是否相同。
Integer m = new Integer(1); // 新new的在堆中
Integer n = 1; // 这个用的是缓冲的常量对象,在方法区
System.out.println(m == n); // false
new Integer(1)会强制创建一个新的Integer对象,而n = 1由于值在缓存范围内,会引用缓存中的Integer对象。所以m和n是两个不同的对象。即使它们的值相同,==比较的是引用,而不是值,因此结果为false。
Integer x = new Integer(1); // 新new的在堆中
Integer y = new Integer(1); // 另一个新new的在堆中
System.out.println(x == y); // false
new Integer(1)会创建两个不同的Integer对象,x和y指向不同的内存位置。即使它们的值相同,==比较的是对象引用,而不是对象的值。因此,x == y返回false。
Double d1 = 1.0;
Double d2 = 1.0;
System.out.println(d1==d2); // false 比较地址,没有缓存对象,每一个都是新new的
Double没有像Integer那样有值缓存池,==运算符比较的是对象引用,而不是值。即使d1和d2的值相同,它们仍然是不同的对象,因此d1 == d2返回false。
类型转换问题
Integer i = 1000;
double j = 1000; // 或 1000.0
System.out.println(i == j); // true,在进行 == 比较时,Java会进行自动拆箱,将 Integer 拆箱为 int,并且将 int 转换为 double。最终比较的是数值,而不是对象引用。
Integer i = 1000;
int j = 1000;
System.out.println(i == j); // true
Integer i = 1;
Double d = 1.0
System.out.println(i == d); // 编译报错,Java 不允许直接比较不同类型的包装类对象,即使它们的值相同。因此,编译器会报错,提示类型不匹配。如果你想要比较它们的数值,需要将它们转换成相同的类型。可以通过拆箱和类型转换来实现。
✨包装类对象不可变
一旦包装类对象被创建,它内部保存的值就不能再被修改。如果看起来“修改”了包装类对象,其实是创建了一个新的对象。
public class WrapperTest {
public static void main(String[] args) {
Integer num = 10;
System.out.println("调用前 num = " + num);
changeValue(num);
System.out.println("调用后 num = " + num);
}
public static void changeValue(Integer x) {
x = 20; // 实际上是让 x 指向一个新的 Integer 对象
System.out.println("方法内 x = " + x);
}
}
调用前 num = 10
方法内 x = 20
调用后 num = 10
public class Test2 {
public static void main(String[] args) {
Integer i = 5;
increment(i);
System.out.println(i); // 输出还是 5
}
public static void increment(Integer x) {
x++; // x++ 会创建一个新的 Integer 对象
}
}
1497

被折叠的 条评论
为什么被折叠?



