一.什么是包装类
众所周知,java是一门面向对象的语言,java中,有8种基本数据类型,这8种基本类型似乎不符合java面向对象的特点。为了保证java中“一切皆对象”的原则,java给每一个基本类型都分别整了一个类对这8种基本数据类型进行包装,这就是包装类。
二.八个基本数据类型的包装类
基本数据类型 --- 包装类
byte --- Byte short --- Short int --- Integer long --- Long
float --- Float double --- Double char --- Character boolean --- Boolean
三.包装类的装箱和拆箱
装箱:将基本数据类型变为包装类,每个包装类的构造方法都可以接收各自数据类型的变量
拆箱:将包装类转换为基本数据类型,就是从包装类中取出被包装的数据
自动拆箱和自动装箱:就是指拆箱和装箱操作都是自动执行的
根据上述可知,基本数据类型和包装类是可以进行相互转换的
我们来看这样一行代码:
Integer a = 10;
//在这行代码中,就进行了自动装箱
//这行代码在执行的时候在jvm中会编写为Integer a = Integer.valueOf(10);
//将10这一基本数据类型转换为了包装类,赋值给b
int a = b;
//这一行代码中,进行了自动拆箱,将包装类的b转换为基本类型int,赋值给了a
四.128陷阱
什么是128陷阱?我们先来看这样一段代码:
Integer a = 100;
Integer b = 100;
System.out.println(a==b);
Integer a1 = 128;
Integer b1 = 128;
System.out.println(a1==b1);
这段代码输出为第一行:true 第二行:false
为什么呢?这就需要我们去看底层代码,上面我们说到 Integer a = 100;实际是在执行Integer a = Integer.valueOf(100); 我们去看看这个Integer中的valueOf();方法是怎么写的:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
这段代码的意思就是,当我传入的int类型的参数,大于一个最小值且小于一个最大值的时候,直接返回一个值,当不在这个区间范围内的时候,就需要在堆内存中new一个新的内存空间。
当值在最小值和最大值之间的时候返回的东西到底是什么呢?这个最大值和最小值又是多少呢?我们来深挖一下,这里我们把返回值中的IntegerCache的源码搬来看看:
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer[] cache;
static Integer[] archivedCache;
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
h = Math.max(parseInt(integerCacheHighPropValue), 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(h, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
// Load IntegerCache.archivedCache from archive, if possible
CDS.initializeFromArchive(IntegerCache.class);
int size = (high - low) + 1;
// Use the archived cache if it exists and is large enough
if (archivedCache == null || size > archivedCache.length) {
Integer[] c = new Integer[size];
int j = low;
for(int i = 0; i < c.length; i++) {
c[i] = new Integer(j++);
}
archivedCache = c;
}
cache = archivedCache;
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
这段代码完整的看下来可能也是云里雾里,我们只需要看懂一部分就行了,首先这个最大值和最小值很容易就能找得到,最大值是127,最小值是128,返回的那个cache数组我们只需要看上述代码截取的这一段代码:
int size = (high - low) + 1;
if (archivedCache == null || size > archivedCache.length) {
Integer[] c = new Integer[size];
int j = low;
for(int i = 0; i < c.length; i++) {
c[i] = new Integer(j++);
}
archivedCache = c;
}
cache = archivedCache;
嘿,这段代码不就是给这个数组进行初始化吗,很难不看出这就是一个长度为256的,存储着-128到127的数组。
这一切都解释得清了,答案就在Integer的valueOf()方法之中,如果我们传入的数值在-128到127之间,都会存储在一个cache数组之中,这个数组就相当于一个缓存,当我们在-128到127之间进行自动填箱的时候,我们就直接返回cache数组之中这个数所在的地址,因此我们刚开始的 Integer a = 100;Integer b = 100;中,a和b存储的地址是相同的,输出true。但是如果传进的参数不在这个范围之内的话,就需要在堆内存中开辟一个新的内存空间,因此Integer a1 = 128;Integer b1 = 128;中,a1和b1存储的地址就是不同的,输出是false。
五.包装类小练习
看一看下面代码的输出结果:
int a = 100;
int b = 100;
Integer a1 = 100;
Integer b1 = 100;
Integer a2 = new Integer(100);
Integer b2 = new Integer(100);
System.out.println("1:"+ (a==b));
System.out.println("2:"+ (a1==b1));
System.out.println("3:"+ (a2==b2));
System.out.println("4:"+ (a1==a));
System.out.println("5:"+ (a1.equals(a)));
System.out.println("6:"+ (a1==a2));
System.out.println("7:"+ (a==a2));
输出结果为:
解释:1.第一行不需要进行解释
2.第二行中100是在-128到127之间的数,因此a1和b1存储的都是cache数组中100的地址,是相同的,返回值为true。
3.第三行中a2和b2都是各自在堆内存中开辟了一块新地址,两块地址肯定是不同的,返回值为false。
4.第四行中 包装类用 == 和基本数据类型进行比较的时候会自动拆箱,就相当于两个int类型相比较,返回值自然为true。
5.第五行中 a1.equals();方法需要传入一个Object类型的数据,因此a会进行自动装箱,这里我们把Integer.equals方法的源码搬来看一看,value值相等就返回true,因此返回值为true。
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return this.value == (Integer)obj;
} else {
return false;
}
}
6.第六行中 a1中存储的地址是cache数组中100的地址,a2中存储的地址是在堆内存中开辟的内存的地址,两个地址肯定不同,故返回值为false。
7.第七行中 a2会进行自动拆箱,转换为两个基本数据类型int相比,返回值为true。