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

  自动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来一些看一下装箱和拆箱中的若干问题。本文先讲述装箱和拆箱最基本的东西,再来看一下面试笔试中经常遇到的与装箱、拆箱相关的问题。

  以下是本文的目录大纲:

  一.什么是装箱?什么是拆箱?

  二.装箱和拆箱是如何实现的

  三.面试中相关的问题

  若有不正之处,请谅解和批评指正,不胜感激。

  请尊重作者劳动成果,转载请标明原文链接:

  http://www.cnblogs.com/dolphin0520/p/3780005.html

一.什么是装箱?什么是拆箱?

  在前面的文章中提到,Java为每种基本数据类型都提供了对应的包装器类型,至于为什么会为每种基本数据类型提供包装器类型在此不进行阐述,有兴趣的朋友可以查阅相关资料。在Java SE5之前,如果要生成一个数值为10的Integer对象,必须这样进行:

1
Integer i = new Integer(10);

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

1
Integer i = 10;

  这个过程中会自动根据数值创建对应的 Integer对象,这就是装箱。

  那什么是拆箱呢?顾名思义,跟装箱对应,就是自动将包装器类型转换为基本数据类型:

1
2
Integer i = 10;  //装箱
int n = i;   //拆箱

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

  下表是基本数据类型对应的包装器类型:

int(4字节) Integer
byte(1字节) Byte
short(2字节) Short
long(8字节) Long
float(4字节) Float
double(8字节) Double
char(2字节) Character
boolean(未定) Boolean

二.装箱和拆箱是如何实现的

  上一小节了解装箱的基本概念之后,这一小节来了解一下装箱和拆箱是如何实现的。

  我们就以Interger类为例,下面看一段代码:

1
2
3
4
5
6
7
public class Main {
    public static void main(String[] args) {
         
        Integer i = 10;
        int n = i;
    }
}

  反编译class文件之后得到如下内容:

  

  从反编译得到的字节码内容可以看出,在装箱的时候自动调用的是Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法。

  其他的也类似,比如Double、Character,不相信的朋友可以自己手动尝试一下。

  因此可以用一句话总结装箱和拆箱的实现过程:

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

三.面试中相关的问题

  虽然大多数人对装箱和拆箱的概念都清楚,但是在面试和笔试中遇到了与装箱和拆箱的问题却不一定会答得上来。下面列举一些常见的与装箱/拆箱有关的面试题。

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

1
2
3
4
5
6
7
8
9
10
11
12
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);
    }
}

  也许有些朋友会说都会输出false,或者也有朋友会说都会输出true。但是事实上输出结果是:

true
false

  为什么会出现这样的结果?输出结果表明i1和i2指向的是同一个对象,而i3和i4指向的是不同的对象。此时只需一看源码便知究竟,下面这段代码是Integer的valueOf方法的具体实现:

public static Integer valueOf(int i) {
if(i >= -128 && i <= IntegerCache.high)
return IntegerCache.cache[i + 128];
else
return new Integer(i);
}

  而其中IntegerCache类的实现为:

 private static class IntegerCache {
static final int high;
static final Integer cache[]; static {
final int low = -128; // high value may be configured by property
int h = 127;
if (integerCacheHighPropValue != null) {
// Use Long.decode here to avoid invoking methods that
// require Integer's autoboxing cache to be initialized
int i = Long.decode(integerCacheHighPropValue).intValue();
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - -low);
}
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() {}
}

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

  上面的代码中i1和i2的数值为100,因此会直接从cache中取已经存在的对象,所以i1和i2指向的是同一个对象,而i3和i4则是分别指向不同的对象。

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

1
2
3
4
5
6
7
8
9
10
11
12
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);
    }
}

  也许有的朋友会认为跟上面一道题目的输出结果相同,但是事实上却不是。实际输出结果为:

false
false

  至于具体为什么,读者可以去查看Double类的valueOf的实现。

  在这里只解释一下为什么Double类的valueOf方法会采用与Integer类的valueOf方法不同的实现。很简单:在某个范围内的整型数值的个数是有限的,而浮点数却不是。

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

     Double、Float的valueOf方法的实现是类似的。

3.下面这段代码输出结果是什么:

1
2
3
4
5
6
7
8
9
10
11
12
public class Main {
    public static void main(String[] args) {
         
        Boolean i1 = false;
        Boolean i2 = false;
        Boolean i3 = true;
        Boolean i4 = true;
         
        System.out.println(i1==i2);
        System.out.println(i3==i4);
    }
}

  输出结果是:

true
true

  至于为什么是这个结果,同样地,看了Boolean类的源码也会一目了然。下面是Boolean的valueOf方法的具体实现:

public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}

  而其中的 TRUE 和FALSE又是什么呢?在Boolean中定义了2个静态成员属性:

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

    /**
* The <code>Boolean</code> object corresponding to the primitive
* value <code>false</code>.
*/
public static final Boolean FALSE = new Boolean(false);

  至此,大家应该明白了为何上面输出的结果都是true了。

4.谈谈Integer i = new Integer(xxx)和Integer i =xxx;这两种方式的区别。

  当然,这个题目属于比较宽泛类型的。但是要点一定要答上,我总结一下主要有以下这两点区别:

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

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

5.下面程序的输出结果是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Main {
    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));
    }
}

  先别看输出结果,读者自己想一下这段代码的输出结果是什么。这里面需要注意的是:当 "=="运算符的两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。另外,对于包装器类型,equals方法并不会进行类型转换。明白了这2点之后,上面的输出结果便一目了然:

true
false
true
true
true
false
true

  第一个和第二个输出结果没有什么疑问。第三句由于  a+b包含了算术运算,因此会触发自动拆箱过程(会调用intValue方法),因此它们比较的是数值是否相等。而对于c.equals(a+b)会先触发自动拆箱过程,再触发自动装箱过程,也就是说a+b,会先各自调用intValue方法,得到了加法运算后的数值之后,便调用Integer.valueOf方法,再进行equals比较。同理对于后面的也是这样,不过要注意倒数第二个和最后一个输出的结果(如果数值是int类型的,装箱过程调用的是Integer.valueOf;如果是long类型的,装箱调用的Long.valueOf方法)。

如果对上面的具体执行过程有疑问,可以尝试获取反编译的字节码内容进行查看。

  如果有哪位朋友有补充的内容,欢迎下方留言,不胜感激。

深入剖析Java中的自动装箱和拆箱过程的更多相关文章

  1. 《Java中的自动装箱和拆箱功能.》

    //Java中的自动装箱和拆箱功能. class AutoboxingUnboxing { public static void main(String[] args) { //直接把一个基本类型变量 ...

  2. Java中的自动装箱与拆箱

    自动装箱和拆箱从Java 1.5开始引入,目的是将原始类型值转自动地转换成对应的对象.自动装箱与拆箱的机制可以让我们在Java的变量赋值或者是方法调用等情况下使用原始类型或者对象类型更加简单直接. 如 ...

  3. java中的自动装箱和拆箱

    一.什么是自动装箱和拆箱: 我们知道java为8种基本类型分别提供了对应的包装类型,在Java SE5之前,如果要生成一个数值为10的Integer对象,必须这样进行: Integer i=new I ...

  4. java 中的自动装箱和拆箱操作

    在前面的文章中提到,Java为每种基本数据类型都提供了对应的包装器类型,至于为什么会为每种基本数据类型提供包装器类型在此不进行阐述,有兴趣的朋友可以查阅相关资料.在Java SE5之前,如果要生成一个 ...

  5. java -关于包装类自动装箱与拆箱拓展+整形常量池

    关于自动装箱与拆箱 1.包装类与基本数据类型的自动转换,叫装箱和拆箱(类型自动转换) 2.自动装箱拆箱是在编译器,编译器自动配转换方法,实现装箱和拆箱.所以这个过程发生在编译期 3.只有需要相互类型转 ...

  6. Java基础 【自动装箱和拆箱、面试题】

    JDK 1.5 (以后的版本)的新特性自动装箱和拆箱 1. 自动装箱:把基本类型转换为包装类类型 int a =10; Integer i = new Integer(a); Integer valu ...

  7. Java暗箱操作之自动装箱与拆箱

    我以前在写Android项目的时候,估计写得最多最熟练的几句话就是: List<Integer> list = new ArrayList<Integer>(); list.a ...

  8. java包装类,自动装箱,拆箱,以及基本数据类型与字符串的转换

    package cn.learn; import java.util.ArrayList; /* 包装类 java.lang中,基本运算类型效率高 装箱:把基本类型数据包装为包装类 1.构造方法 In ...

  9. Java中的自动装箱拆箱

    Java中的自动装箱拆箱 一.自动装箱与自动拆箱 自动装箱就是将基本数据类型转换为包装类类型,自动拆箱就是将包装类类型转换为基本数据类型. 1 // 自动装箱 2 Integer total = 90 ...

随机推荐

  1. [总结]RTMP流媒体技术零基础学习方法

    本文主要总结一些我在学习RTMP流媒体技术过程中积累的经验.也为后来学习RTMP流媒体技术的人们一个参考.本文力图从简到难,循序渐进的介绍RTMP流媒体技术的方方面面,先从应用说起,逐步深化剖析相关工 ...

  2. jq 图片上传

    1.html <input type="file" class="ImgInput" name="ImgInput"/> 2.j ...

  3. Linux命令:head命令详解

    概述:head命令用于显示文件文字区块 1.格式 head [参数][文件] 2.参数 -q 隐藏文件名 -v 显示文件名 -c<字节> 显示字节数 -n<行数> 显示的行数 ...

  4. java_设计模式_装饰者模式_Decorator Pattern(2016-07-28)

    装饰模式又名包装(Wrapper)模式.装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案. 装饰模式的结构 装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任.换言之,客户 ...

  5. CentOS7开机启动管理systemd简介及使用

    systemd提供更优秀的框架以表示系统服务间的依赖关系实现系统初始化时服务的并行启动,同时达到降低Shell的系统开销的效果systemd的目标是:尽可能启动更少进程:尽可能将更多进程并行启动.sy ...

  6. python的min()函数也可用于比较tuple

      python的min()函数也可用于比较tuple >>> a = (2,'asv','dfg') >>> b = (3,'gsg','weg') >&g ...

  7. github的访问变慢了

    以下个人观点:把操作系统的自主研究还有处理器自主研究列入重点,还有互联网上的种种动作,我发现里面似乎揭示了某些迹象,科研真的不应该以牺牲大部分人的河法全益为代价甚至目的.当某一天win不可能出现在出厂 ...

  8. yii2 日志(log)的配置与使用

    原文地址: http://blog.csdn.net/gao_yu_long/article/details/51732181

  9. 重装Ubuntu系统并配置开发环境

    安装 Ubuntu 并配置开发环境 写一篇文章详细记录下来所有的过程,以便以后参考. 安装前的准备 备份所有代码和配置文件 备份下载的各类文件 Ubuntu 安装 下载安装 Ubuntu14.04,下 ...

  10. 转:PHP开发框架流行度排名:Laravel居首

    原文来自于:http://www.sitepoint.com/best-php-frameworks-2014/ Update: If you’d like to take part in the n ...