文章目录
前言
上一篇博客跟各位Java老屁股聊了lang包到底是什么(点击标题阅览文章),其中第一个提到的常用类就是包装类,那这篇博客就来探讨一下《为什么要有包装类》,它能实现什么基本数据类型所实现不了的?接下来就一一说明。
一、包装类是什么
包装类就是把基本数据类型包装成对象(即引用数据类型),使得它们也具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。
| 基本数据类型 | 对应的包装类(引用数据类型) |
|---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
继承关系:除了 Character 和 Boolean 之外,其他所有的包装类都继承自 Number 类。这种继承关系使得这些包装类能够共享一些通用的功能和特性,例如数字的比较和转换,这为在不同数值类型之间的操作提供了一致的接口。

查看继承关系的快捷键:
-
Ctrl + h 快捷键可以查看当前类的继承关系
-
或者在源码界面右键点击“图表”,再点击“显示图”
如果操作所示:

-
以 Integer 为例,继承关系图如下

二、泛型要求
泛型底层用 Object 来存数据,取出来时再强转成对应的类型。Object是所有类的父类,它只能接收 引用数据类型(对象),不能存 基本数据类型,所以必须用包装类(Integer、Double、Character)把基本数据类型包装成对象。
//ArrayList源码,说明集合底层是个Object[]集合
transient Object[] elementData; // non-private to simplify nested class access
//取出元素时,把Object强转为泛型类型E
E elementData(int index) {
return (E) elementData[index];
}
三、null 值表示
- 阿里巴巴开发手册强制规定

3.1 POJO 属性:用 null 强制提醒
如果一个订单的金额属性是 int,没有给它赋值,会使用默认值 0,变成“零元订单”,这个 Bug 很难发现。
但如果金额属性是 Integer,没有赋值它就是 null。当你后续使用它时,如果没有做非空判断,程序会立刻抛出 NullPointerException(NPE),强制性地提醒你:这里忘记赋值了,必须修复。
3.2 RPC 方法:用 null 表示调用失败
手册中举了一个反例:成交总额涨跌幅 x 如果用基本类型接收,当远程调用失败时,x 会被赋成默认值 0,页面就会显示“0%”,而正确做法应该是显示中划线。
包装类型的 null 恰好能承载“远程调用失败”这个额外信息,让调用方有机会区分“正常返回 0”和“调用出错了”。
3.3 Controller 方法参数:用 null 表示前端没传
- 错误理解
有人可能认为:基本数据类型默认值为 0,而包装类默认值为 null。所以在业务场景中,用户没填年龄,如果使用 Integer 就会传默认值 null;使用 int 就会传默认值 0。此时年龄应该用 null 表示,而 0 代表年龄为 0 岁不合适,所以我们要用包装类。 - 正确理解
1. 成员变量和局部变量的默认值规则完全不同
成员变量:int 默认是 0,Integer 默认是 null。
局部变量:必须显式初始化,没有默认值。
但 Controller 方法的参数并不是类的成员变量,而是局部变量,需要由 Spring 从请求中绑定数据,没有默认值这一说。所以不能用“int 默认值是 0”来推断“前端不传参就能得到 0”。
2. Spring 绑定参数
当前端不传某个参数时,Spring 拿到的值是 null。
- 如果使用基本类型,Spring 拿到的绑定值是 null,但 int 不是对象,没法接收 null,会直接抛出NPE异常;
- 而使用包装类,Spring 可以将 null 正常绑定给包装类型,所以 age 为 null。
//报错:int不能存null
int age = null;
//Integer 可以存 null
Integer age = null;
3. 配合动态 SQL,安全更新数据库
- 错误写法
如果前端没传这个参数,会直接把数据库里的 age 字段更新为 null,覆盖掉原来的值。
UPDATE User
<set>
age = #{age}
</set>
WHERE id = #{id}
- 正确写法
此时,age 为 null 时 if 条件不成立,不会更新年龄字段,完美保留了旧值。
UPDATE User
<set>
<if test="age != null">
age = #{age}
</if>
</set>
WHERE id = #{id}
四、缓存机制
-
作用
包装类的缓存机制是为了优化性能和节省内存而设计的。
它为整型(Byte、Short、Integer、Long)、字符型(Character)和布尔型(Boolean)的包装类都提供了缓存,创建这个范围的整数时,不会新建对象,而是直接返回缓存池里的同一个对象。
而对于浮点数类型的包装类(Float、Double),则没有这种缓存机制,意味着每次都需要创建新的对象。 -
使用条件
该缓存池适用于调用了valueOf()方法产生的包装类对象,以及自动装箱的包装类对象;而不适用于new关键字创建的包装类对象。 -
缓存范围
Integer:-128 到 127
Byte:-128 到 127
Short:-128 到 127
Character:0 到 127
Boolean:true 和 false
Integer i1 = 127;
Integer i2 = 127;
System.out.println(i1 == i2);//true
Integer i3 = 128;
Integer i4 = 128;
System.out.println(i3 == i4);//false
//注意:用 equals 比较的是数值本身,无法体现是否是同一个对象
//而双等号== 比较的是地址,可以体现是同一个对象
System.out.println(i3.equals(i4));//true
拓展
自定义缓存的大小: 通过 JVM 参数 -XX:AutoBoxCacheMax=size 可以设置 Integer 缓存的最大值,最小缓存值固定为 -128,不可修改。
-
在当前文件下点击”编辑配置“

-
左侧选中你要运行的主类或Spring Boot 启动类。

-
点击修改选项,再点击添加虚拟机选项

-
名称中填类名,主类填完整类名(包括包名),虚拟机选项填-XX:AutoBoxCacheMax=最大值

- 最后效果
Integer i1 = 128;
Integer i2 = 128;
System.out.println(i1 == i2);//true
Short s1 = 128;
Short s2 = 128;
System.out.println(s1==s2);//false
//AutoBoxCacheMax 只对 Integer 生效
五、类型转换
- 把字符串数值转换成对应的基本数据类型
八种包装类当中,除了 Character 都有对应的parseXxx的方法,进行类型转换。
注意:字符串的内容必须是数字,如果是其它类型比如字母"abcd",则会报错。
String s1 = "666";
int i1 = Integer.parseInt(s1);
int i2 = Integer.valueOf(s1);
System.out.println(i1);//666
System.out.println(i2);//666
String s2 = "555.5";
double i3 = Double.parseDouble(s2);
System.out.println(i3);//555.5
- 把基本类型的数据转换成字符串
int i = 3;
String s = Integer.toString(i);//"3"
System.out.println(s+1);//"31"
//拓展:转换成字符串的其它方法
int i = 3;
//1.
String s = i + "";
//2.
String s = String.valueOf(i);
六、Character 类的方法
Character 没有 parseXxx,如果想将字符串类型的数据转换成 char 类型的数据,可以通过 String 类中的 toCharArray() 方法和 charAt() 方法实现。
除此之外,它还提供了一系列静态方法来判断字符类型和做大小写转换。
| 方法名 | 说明 |
|---|---|
isDigit(char ch) | 是否是数字 |
isLetter(char ch) | 是否是字母 |
isLetterOrDigit(char ch) | 是否是字母或数字 |
isWhitespace(char ch) | 是否是空白字符(空格、tab、换行) |
isUpperCase(char ch) | 是否是大写字母 |
isLowerCase(char ch) | 是否是小写字母 |
toUpperCase(char ch) | 转为大写字母 |
toLowerCase(char ch) | 转为小写字母 |
Character.isDigit('5'); // true
Character.isLetter('a'); // true
Character.isUpperCase('A'); // true
Character.toLowerCase('Q'); // 'q'
七、Integer 类的方法
| 方法名 | 说明 |
|---|---|
public static String toBinaryString(int i) | 返回无符号二进制字符串表示 |
public static String toOctalString(int i) | 返回无符号八进制字符串表示 |
public static String toHexString(int i) | 返回无符号十六进制字符串表示 |
八、数值常量
MAX_VALUE / MIN_VALUE
System.out.println(Integer.MAX_VALUE); // 2147483647
System.out.println(Integer.MIN_VALUE); // -2147483648
System.out.println(Double.MAX_VALUE); // 1.7976931348623157E308
九、包装类的注意事项
1. == 和 .equals():== 比较的是对象引用,.equals() 比较的是值。
2. 空指针异常(NullPointerException):
Integer num = null;
int value = num; //抛出 NullPointerException
3. 性能开销: 装箱和拆箱涉及对象创建和方法调用,有性能开销。在性能敏感的循环中,应尽量避免不必要的装箱拆箱。
4. 内存占用:一个 Integer 对象比一个 int 占用多得多的内存(约 24 字节 vs 4 字节)。
总结
简单来说,对外输入输出的地方用包装类型,内部临时变量用基本类型。 有什么问题欢迎在评论区指出!!! 要是能点个赞我就更开心了^_^


776

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



