java double 装箱_Java自动拆箱和装箱

一、什么是装箱/拆箱

在讲之前,得先提一下为什么两个概念:基本数据类型及其包装类,我们都知道Java是一种面向对象的语言,但是Java中的基本数据类型是不面向对象的,这时在使用中便会存在诸多的不便,为了解决这个不足,在设计类时为每个基本数据类型设计了一个对应的包装类(Wrapper Class),他们之间的对应关系如下表:

基本类型

包装类

boolean

Boolean

char

Character

byte

Byte

short

Short

int

Integer

long

Long

float

Float

double

Double

void

Void

弄清楚了这个概念之后,我们回到正题,在Java SE5之前,如果要生成一个数值为10的Integer对象,只需要这样就可以了:

Integer i = new Integer(10);

而从Java SE5开始就提供了自动装箱的特性,如果生成一个数值为10的Integer对象,只需这样就可以了:

Integer i = 10;

这个过程中会自动根据数值创建对应的Integer对象,这就是装箱。那么什么是拆箱呢,顾名思义,跟装箱对应,就是自动将包装器类型转换为基本数据类型:

Integer i = 10 //装箱

int n = i; //拆箱

简单一点说,装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型。

二、拆箱与装箱是如何实现的?

以下面这段代码为例:

public class Main {

public static void main(String[] args){

Integer i = 10;

int n = i;

}

}

利用IDEA对其进行反编译:

7939a6bbaae6

从我圈出来的地方可以看出,在装箱的时候调用的是Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法。

其他的也类似,如Double,Character,在这里就不展示截图了。

由此可得出结论:

装箱过程是通过调用包装器的valueOf方法实现的,二拆箱是通过调用包装器的xxxValue方法实现的。(xxx代表对应的基本数据类型)

三、总结一些常见的面试题

虽然到这里你已经明白的装箱和拆箱的概念,但是如果碰到了相关的考题却不一定能答得上来,下面就来列举一些常见的相关考题:

下面这段代码的输出结果是什么?

public class Main {

public static void main(String[] args){

Integer i1 = 100;

Integer i2 = 100;

Integer i3 = 200;

Integer i4 = 200;

System.out.println(i1==i2);

System.out.println(i3==i4);

}

}

先直接上结果吧:

7939a6bbaae6

分析:

首先,在创建Integer对象的时候,设计到一个自动装箱的过程,从上面的分析我们得知这其中涉及到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);

}

//Integer中的静态内部类

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 =

VM.getSavedProperty("java.lang.Integer.IntegerCache.high");

if (integerCacheHighPropValue != null) {

try {

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);

} catch( NumberFormatException nfe) {

// If the property cannot be parsed into an int, ignore it.

}

}

high = h;

cache = new Integer[(high - low) + 1];

int j = low;

for(int k = 0; k < cache.length; k++)

cache[k] = new Integer(j++);

// range [-128, 127] must be interned (JLS7 5.1.7)

assert Integer.IntegerCache.high >= 127;

}

private IntegerCache() {}

}

从源码中我们可以得知,在通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用,否则创建一个新的Integer对象。

所以上面例子中i1和i2指向的是同一个对象,而i3和i4则分别指向不同的对象。

再来看看下面找个例子:

public class Main {

public static void main(String[] args){

Double i1 = 100.0;

Double i2 = 100.0;

Double i3 = 200.0;

Double i4 = 200.0;

System.out.println(i1 == i2);

System.out.println(i3 == i4);

}

}

是不是在觉得这跟上面那个例子一样一样的?先来看看答案:

7939a6bbaae6

why?先来看看Double的valueOf是怎么实现的:

public static Double valueOf(double d) {

return new Double(d);

}

这下明白结果是怎么来的了吧?还不懂?其实很简单,整数100与101是连续的,但是对于浮点数,100.1与100.2之间有多少个浮点数?无数个!所以这里无法像Integer那样做一个缓存。

到了这里,已经明白了原理,就可以做一个总结了:

Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的,Double、Float的valueOf方法的实现是类似的。

xxxCache存在的意义:缓存缓存,当然是提高效率,避免多次重复创建相同对象了!(当然可能还有其他原因只是我不知道罢了 = =)

继续上例子:

public class Main {

public static void main(String[] args) {

Boolean b1 = false;

Boolean b2 = false;

Boolean b3 = true;

Boolean b4 = true;

System.out.println(b1 == b2);

System.out.println(b3 == b4);

}

}

猛地发现前面只总结7种数据类型,还有最优一种。老规矩先上答案:

7939a6bbaae6

源码:

public static final Boolean TRUE = new Boolean(true);

public static final Boolean FALSE = new Boolean(false);

public static Boolean valueOf(boolean b) {

return (b ? TRUE : FALSE);

}

ok,我们可以看到Boolean内部定义了两个静态常量,可以理解成提前做了缓存吧,只不过bool类型只有true和false,所以没必要像Integer那样做一个cache了。

Integer i = new Integer(xxx)和 Integer i = xxx两种方式的区别

这个题目可以从多个角度切入,但是自动装/拆箱的要点一定要答上,例如:

第一种方式不会触发自动装箱的过程;而第二种方式会触发;

在执行效率和资源占用上的区别。第二种方式的执行效率和资源占用在一般情况下要优于第一种情况(注意这并不是绝对的)。

最后再来一个大练习:

public static void main(String[] args) {

Integer a = 1;

Integer b = 2;

Integer c = 3;

Integer d = 3;

Integer e = 321;

Integer f = 321;

Long g = 3L;

Long h = 2L;

System.out.println(c == d);

System.out.println(e == f);

System.out.println(c == (a + b));

System.out.println(c.equals(a + b));

System.out.println(g == (a + b));

System.out.println(g.equals(a + b));

System.out.println(g.equals(a + h));

}

虽然上面分别讲了下这些包装类的valueOf方法的实现,但是这里可能还是会犯迷糊,下面先提供两点提示:

当==运算符的两个操作数都是包装类的引用时比较的是它们是否指向同一个引用,而如果其中有一个操作数是表达式(包含算术运算)则比较的是数值(即触发自动拆箱的过程)。

对于包装类,equals方法并不会进行类型转换。

下面是运行结果:

7939a6bbaae6

分析:

首先前两个结果相信没有什么异议。第三个由于a+b是一个算术运算,因此会触发自动拆箱,最终比较的是数值,所以结果为true,g == (a + b)同理。

再来说下第四个,首先a+b最终得到的是一个Integer类型的值,然后再执行equals方法,后面两个同理。

四、总结

装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型。

装箱过程是通过调用包装器的valueOf方法实现的,二拆箱是通过调用包装器的xxxValue方法实现的。(xxx代表对应的基本数据类型)

Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的,Double、Float的valueOf方法的实现是类似的。Boolean定义了两个静态常量当做缓存。

xxxCache存在的意义:提高效率,避免多次重复创建相同对象了。

当==运算符的两个操作数都是包装类的引用时比较的是它们是否指向同一个引用,而如果其中有一个操作数是表达式(包含算术运算)则比较的是数值(即触发自动拆箱的过程)。

对于包装类,equals方法并不会进行类型转换。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值