您的位置:首页 > 编程语言 > Java开发

深入剖析Java中的装箱和拆箱

2016-10-15 20:45 253 查看
Java有八种基本数据类型,Java也为每种基本数据类型都提供了相对应的包装器类型。相信不少学过Java的朋友,也都学过Java的拆装箱机制。出于某种原因,今天我又进一步对Java的拆装箱机制进行了较为深入的研究,记下此文,以便时刻自省。

首先先回顾下Java的八种基本数据类型及对应的包装类:

基本数据类型
长度(字节)
包装器类型
byte
1
Byte
short
2
Short
int
4
Integer
long
8
Long
float
4
Float
double
8
Double
char
2
Character
boolean
未定
Boolean
我们从最经典的int类型开始,先看以下关于拆装箱最常见的例子:

Integer i = 3;
int k = i;
像以上的代码,其中第一行即为自动装箱,第二行为自动拆箱。
自动装箱:如上第一行,在执行这行代码时,实际上是在执行Integer i = Integer.valueOf(3);返回给i的,是Integer类型的对象。

自动拆箱:如上第二行,在执行这行代码时,实际上是在执行int k = i.intValue();其中方法intValue()的返回值类
a6e6
型为int。有了这个理论知识,我们看下下面这代码:

Integer i = null;
int k = i;
System.out.println(k);
这段代码的执行结果是什么呢?其实通过自动拆箱的知识,我们就知道在第二行代码就会抛出java.lang.NullPointerException异常了,事实上也是如此。
好了,自动拆装箱也就到这了,不过,下面的知识点你不能不看,不看你在使用Java的自动拆装箱中会遇坑的。

我们再来看以下常见的关于自动拆装箱的面试题:

Integer i = 3;
Integer j = 3;
Integer k = 666;
Integer l = 666;
System.out.println(i == j);
System.out.println(k == l);
Integer easy1 = new Integer(3);
Integer easy2 = new Integer(3);
System.out.println(easy1 == easy2);
会输出什么呢?我们知道,==是比较对象的,同一个对象才会使==的结果为true,所以第九行代码的输出明显就是false,那现在的问题就是i与j,k与l是不是同一个对象了,那它们会不会是同一个对象呢,上面说过了,自动装箱实际上执行的是Integer.valueOf(int i),那我们就来看下valueOf方法到底是怎么执行的,以下是Integer.valueOf(int
i)的源码:
public static Integer valueOf(int i) {
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];

static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
}
high = h;

cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}

private IntegerCache() {}
}
我们可以看来,在默认情况下,Java为我们缓存了[-128,127]的对象,所以在这之间,通过自动装箱得到的,是同一个对象,而在这之外的,就不是同一对象了,当然,从代码可以得知,这个缓存的范围是可以改变的。现在,结果就很清楚了:
true
false
false
那么,下面的结果会是什么呢?
Double i = 3.0;
Double j = 3.0;
Double k = 666.0;
Double l = 666.0;
System.out.println(i == j);
System.out.println(k == l);
我们先来看结果吧:
false
false


什么,这跟说好的不一样,这是为什么呢,我们还是一样,还看下它执行的源码:
public static Double valueOf(double d) {
return new Double(d);
}
我们可以看得出来,Double.valueOf方法并没有像Integer的实现一样,只是简单的返回一个新的对象,为什么会是这样呢,其实这也很好想象,因为Integer是整型,是有限个数的,而浮点型是非有限的。
所以,我们可以知道Integer、Short、Byte、Character、Long的实现是类似的,而Float、Double的实现也是类似的。而对于Boolean类型,我们可以想象出来,Boolean的结果不是true,就是false,所以我们根本没必要在自动装箱时为每个对象实例化一个,不过,我们还是要看下源码:

public static Boolean valueOf(String s) {
return toBoolean(s) ? TRUE : FALSE;
}
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
刚好,实现正是我们所想的。
最后,再来看一个比较的问题,这个问题不是两个包装器类型比较,两个数据类类型比较可以也应该用equals()方法

看以下代码:

Integer e1 = 666;
System.out.println(e1 == 666);
这里会输出什么呢?其实这个问题就在于,基本数据类型与对应的类类型比较时,是自动拆箱后比较,还是自动装箱后比较?我们在第二行代码处打一个断点,然后debug模式运行:
public int intValue() {
return value;
}
我们看到,程序调用了intValue()方法,也就拆箱后比较,也就是666==666,结果当然是true了。这也说明了基本数据类型与对应的类类型比较时,是类类型拆箱后比较,即比较的是数值,不过这样我们就要当心了,当类类型为null时,可是会出现空指针异常的喔。

好了,今天的分享就到这里了,由于是个人经验总结,难免会出错,欢迎大家批评指正,万般感谢。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: