什么是自动拆箱和自动装箱?
介绍之前,先介绍两个基本概念:基本数据类型及其包装类。Java是一种面向对象的语言,但是Java中的数据类型并不是面向对象的,为此每个数据类型都对应有个包装类,对应关系如下表:
包装类的作用:
提供字符串、基本数据类型、对象之间的相互转换关系
包含每种基本数据类型的相关属性,如最大值、最小值
自动装箱:将基本数据类型转化为包装类。
Integer a=new Integer(100);//自动装箱
//等价于 integer a=integer.valueof(100);
自动拆箱:将包装类转化为基本数据类型。
int b=a;//自动拆箱。编译器会修改为:int b=a.intevalue();
总结:
装箱过程是通过调用包装器的valueOf方法实现的,二拆箱是通过调用包装器的xxxValue方法实现的。(xxx代表对应的基本数据类型)
常见问题
//integer缓存[-128,127]之间的数字。实际上是系统初始化的时候,创建了[-128,127]之间的一个缓存数组。
//当调用valueOf()的时候,首先检查是否在[-128,127]之间,如果在这个范围内则直接从缓存数组中拿出已经存在在integer对象。
//否则,创建一个新的integer对象。
Integer i1=Integer.valueOf(-128); //等价于Integer i1=-128;
Integer i2=-128;
System.out.println(i1==i2);//true,因为-128在缓存范围内
System.out.println(i1.equals(i2));
System.out.println("------");
Integer i4=1234;
Integer i5=1234;
System.out.println(i4==i5);//false,因为1234不在缓存范围内,所以new了一个新对象
System.out.println(i4.equals(i5));
System.out.println("------");
为什么第一个i1==i2为True,第二个i4==i5为false。我们都知道==是判断两个对象是否相同,.equals是判断两个值是否相同。Integer i2=-128;在创建Integer对象时,实际上也是自动装箱的过程。因此我们需要查看Integer的ValueOf方法。
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
VM.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() {}
}
@HotSpotIntrinsicCandidate
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
通过上面的代码分析,我们可以看到Integer的缓存数组是在[-128,127]之间,如果数值在[-128,127]之间,则返回指向IntegerCache.cache中已经存在的对象,否则则创建一个新的对象。
所以i1和i2指向同一个对象,i4和i5指向不同的对象。
上面的例子是针对Integer类的,如果换成Double类呢?大家想想下面的代码会输出什么结果。
public class TestAutoBox2 {
public static void main(String[] args){
Double i1 = 1.0;
Double i2 = 1.0;
Double i3 = 20.0;
Double i4 = 20.0;
System.out.println(i1 == i2);
System.out.println(i3 == i4);
}
}
其实都是false。为什么会使这样的结果呢。我们需要去查看Double.valueof函数。
public static Double valueOf(double d) {
return new Double(d);
}
我们可以看到,每次创建double对象时,都在重新创建一个新的对象。整数1到整数2之间是连续的,浮点数1,0到2.0之间有无穷多数,所以无法像integer那样建立一个IntegerCache缓存数组。
总结:
Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的,Double、Float的valueOf方法的实现是类似的。
xxxCache存在的意义:缓存缓存,当然是提高效率,避免多次重复创建相同对象了!
上面总结只总结了7种数据类型。对于Boolean类型呢?
public class TeatAutoBox3 {
public static void main(String[] args) {
Boolean a=Boolean.valueOf(true);
Boolean b=Boolean.valueOf(true);
Boolean c=Boolean.valueOf(false);
Boolean d=Boolean.valueOf(false);
System.out.println(a==b);
System.out.println(c==d);
}
}
输出结果都是true。同理,我们还是查看Valueof函数。
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
我们可以看到valueof函数返回的是两个静态常量。
最后总结:
- 装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型
- 装箱过程是通过调用包装器的valueOf方法实现的,二拆箱是通过调用包装器的xxxValue方法实现的。(xxx代表对应的基本数据类型
- Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的,Double、Float的valueOf方法的实现是类似的。Boolean定义了两个静态常量当做缓存。
- xxxCache存在的意义:提高效率,避免多次重复创建相同对象了。
- 当==运算符的两个操作数都是包装类的引用时比较的是它们是否指向同一个引用,而如果其中有一个操作数是表达式(包含算术运算)则比较的是数值(即触发自动拆箱的过程)。
- 对于包装类,equals方法并不会进行类型转换。
参考链接:https://www.jianshu.com/p/7939a6bbaae6