`

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

 
阅读更多
转自
http://www.cnblogs.com/dolphin0520/p/3780005.html

    自动装箱和拆箱问题是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对象,这就是装箱。
  那什么是拆箱呢?顾名思义,跟装箱对应,就是自动将包装器类型转换为基本数据类型:
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类为例,下面看一段代码:

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.下面这段代码的输出结果是什么?

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.下面这段代码的输出结果是什么?
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.下面这段代码输出结果是什么:
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.下面程序的输出结果是什么?
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自动装箱拆箱深入剖析

    Java中的自动装箱和拆箱是自J2SE 5.0版本引入的特性,它极大地简化了基本数据类型与其对应的包装类之间的转换过程。基本数据类型包括int、double、boolean等,而包装类如Integer、Double和Boolean分别对应这些基本...

    深入理解Java虚拟机++JVM高级特性与最佳实践1

    最后,书本深入剖析了Java实现高效并发的原理,包括内存模型、线程同步、原子性、可见性和有序性等概念,这些都是多线程编程中必须掌握的关键知识。 总的来说,《深入理解Java虚拟机++JVM高级特性与最佳实践1》是一...

    java的艺术(The art of java)(普通下载).pdf

    本书不仅仅介绍了Java的基础语法和特性,更重要的是,它深入剖析了Java的设计理念以及这些设计理念如何体现在具体的语言特性和API设计中。 #### 二、核心章节概述 1. **简单类型与对象:正确的平衡** - **知识点*...

    深入理解Java虚拟机-文前1

    4. 第四部分讨论了程序的编译与优化,讲解了如泛型、自动装箱拆箱等语法糖的实现,以及JVM的热点探测、即时编译器(JIT)的工作方式,有助于理解代码执行效率的提升。 5. 最后一部分探讨了Java并发编程的实现,涵盖...

    JAVA.5.0.TIGER程序高手秘笈

    《JAVA.5.0.TIGER程序高手秘笈》是一本深入探讨Java 5.0(也称为Java Tiger)编程技术的专业书籍,属于...《JAVA.5.0.TIGER程序高手秘笈》作为一本高级教程,无疑会深入剖析每个主题,并提供丰富的示例和实践指导。

    JAVA视频教程.pdf

    最后,教程还涵盖了常用的类,如包装类、Date类的使用,以及自动装箱和拆箱的原理,这些都是Java编程中不可或缺的知识点。 通过这个详尽的Java视频教程,学习者将能够全面理解Java编程的基础和高级概念,为未来开发...

    最新Java面试题视频网盘,Java面试题84集、java面试专属及面试必问课程

    装箱和拆箱.mp4 │ Java面试题07.==和equals的区别.mp4 │ Java面试题08.String.mp4 │ Java面试题09.讲一下java中的集合.mp4 │ Java面试题10.ArrayList LinkedList.mp4 │ Java面试题11.HashMap和HashTable的区别....

    Oracle---Fundamentals of the Java Programming Language, Java SE 6 Ed 1 架构体系文档

    文档深入剖析了这些新特性的实现原理及其在实际开发中的应用策略,为开发者提供了丰富的实践指导。 ### 三、Java SE 6的类库与API 文档还详细介绍了Java SE 6中的核心类库与API,包括JFC/Swing图形用户界面组件、...

    100多道经典的JAVA面试题及答案解析

    本文旨在深入剖析一份名为“100多道经典的JAVA面试题及答案解析”的文档内容,重点聚焦于面向对象编程(OOP)的基本概念及其在Java中的应用。尽管文档标签标示为"C++",但其主要内容涉及Java语言的核心特征与面试...

    赵劼优秀课件

    ### 关于赵劼及其优秀课件资源 赵劼(老赵,Jeffrey Zhao)...总的来说,赵劼的这份课件是对 Java 和 C# 的一次深入剖析,不仅有助于理解两种语言的核心特性,也为那些正在寻求最佳实践的开发者提供了宝贵的参考意见。

    疯狂Java面试题

    以上只是《疯狂Java面试题》中可能涵盖的部分关键知识点,实际内容会更详尽,包括对每个知识点的深度剖析和面试常见问题。通过学习和掌握这些内容,Java开发者可以更好地应对各种面试挑战,提升自己的技术水平。

    Java面试资料精讲分析.txt

    ### Java面试资料精讲分析 #### 一、程序员面试技巧概览 在IT行业中,面试是程序员...综上所述,本文档全面覆盖了Java程序员面试准备的关键知识点,并深入剖析了每个知识点的细节,希望对正在准备面试的您有所帮助。

    java 面试葵花宝典

    这本书旨在帮助Java程序员在求职过程中更好地理解和应对各种面试问题,提升自己的竞争力。 1. **Java基础知识** - 类与对象:深入理解面向对象特性,如封装、继承、多态。 - 内存管理:垃圾回收机制,JVM内存模型...

    C#.NET技术内幕

    在本文中,我们将围绕C#.NET这一主题,深入剖析其核心概念、语法特性、框架结构以及在实际开发中的应用。 C#,全称CSharp,是由微软公司推出的面向对象的编程语言,主要用于.NET Framework平台。它结合了C++的强...

    androidjava源码-Android-Knowledge:Android源码和Java知识总结

    - **数据类型与运算符**:深入理解基本数据类型、运算符,以及自动装箱拆箱原理。 - **类与对象**:封装、继承、多态等面向对象特性,以及构造器、this与super的用法。 - **集合框架**:ArrayList、LinkedList、...

Global site tag (gtag.js) - Google Analytics