一、Java 数据类型概述
在介绍包装类之前先说一下八种基本数据类型和三种引用数据类型:
1. 基本数据类型(8种)
Java中的8种基本数据类型是语言的基础构建块,它们直接存储值而不是引用:
数据类型 | 大小 | 默认值 | 取值范围 | 包装类 |
---|---|---|---|---|
byte | 8位 | 0 | -128 ~ 127 | Byte |
short | 16位 | 0 | -32768 ~ 32767 | Short |
int | 32位 | 0 | -2³¹ ~ 2³¹-1 | Integer |
long | 64位 | 0 | -2⁶³ ~ 2⁶³-1 | Long |
float | 32位 | 0.0f | IEEE 754标准 | Float |
double | 64位 | 0.0d | IEEE 754标准 | Double |
char | 1位 | '\u0000' | 0 ~ 65535 | Character |
boolean | 16位 | false | true/false | Boolean |
2. 引用数据类型(3种)
类(Class) - 如String、自定义类等
接口(Interface)
数组
二、包装类的必要性
众所周知,Java号称“万物皆对象”,但是这里我们要清楚,八大数据类型所定义的数据不能叫对象,而三种引用数据类型定义出来的数据是对象,下面举个例子:
int age = 18;
//这里的age是变量;而下面的cat 是对象
Cat cat = new cat();
为了让这些基本类型也能以对象的形式存在,于是有了包装类,包装类就可以看作是对基本数据类型的一个补充。
包装类的出现主要解决了以下问题:
对象化需求:使基本类型能参与面向对象的操作
泛型支持:Java泛型不支持基本类型,如List<int>是非法的,必须使用List<Integer>
功能扩展:为基本类型添加实用方法,如Integer.parseInt()
null值支持:基本类型不能为null,包装类可以表示缺失值
三、基本数据类型与包装类的对应关系
在上面介绍8种基本数据类型中以及提到了每种数据类型对应的包装类,这里单独又拿出来,如下图中,左侧为基本数据类型,右侧为其对应的包装类
下面以int类型的数据为例
Integer b = 10;
int a = b;
我们将它反编译一下 然后来分析:
我们可以看到 首先调用了Integer.valueOf方法,然后调用了Integer.intValue方法。
四、自动装箱与自动拆箱
自动装箱(Autoboxing)和拆箱(Unboxing)是Java 5引入的特性,极大简化了基本类型和包装类之间的转换。
刚才提到了Integer.valueOf方法和Integer.intValue方法,那么它们是用来干嘛的呢?它们是用来做自动装箱和自动拆箱的。什么是自动装箱和自动拆箱呢?我们继续来分析:
Integer a = 10; // Integer.valueOf(10); Integer是一个类,类要调用一些方法来存取数据 //自动装箱
int b = a; //a.intValue(); //自动拆箱
在 Integer a = 10 ; 由于Integer本身是一个类,不能像基本数据类型一样直接定义数据,所以只能调用相关的方法,相当于把当前的变量装进一个箱子里进行存储一样。所以叫自动装箱
而在 int b = a; 当要取走a 这个数据的时候,因为int是可以直接存储数据的,所以此时就需要把变量从箱子里拿出来赋给int类型,所以叫自动拆箱。
五、128陷阱深度解析
下面用一道题来更好地理解自动拆装箱:
1. 现象重现
public class Test {
public static void main(string[] args){
Integer a = 100;
Integer b = 100;
Integer c = 200;
Integer d = 100;
System.out.println(a == b);
System.out.println(c == d);
}
结果是
true
false
那么为什么呢?
2. 原因分析
这里我们要清楚:对基本数据类型而言:==判断值是否相等,引用数据类型:==判断地址是否相同,那么这个结果说明a,b指向了同一块内存空间,cd没有指向同一块内存空间。
Integer a = 100; 其实就等价于Integer.valueOf(100) ,接下来就来到了关键部分:我们看看valueOf方法干了些什么:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
这里其实就是Java出于性能考虑,对-128到127的Integer对象进行了缓存。当自动装箱的值在这个范围内时,直接从缓存中返回已存在的对象;超出这个范围时,则新建Integer对象。所以c和d超出了范围,所以新建了对象,那么新建之后地址肯定就不会相同 所以输出了false。
以上问题呢就是一道经典的面试题:128陷阱。
六、使用注意事项
性能考虑:包装类对象比基本类型占用更多内存
空指针风险:包装类可能为null,拆箱时可能抛出NullPointerException
比较陷阱:==比较的是对象引用而非值
缓存范围:不同包装类缓存范围不同:
Byte: -128~127(全部缓存)
Short/Integer/Long: -128~127
Character: 0~127
Boolean: true/false(全部缓存)