热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【转】深入剖析Java中的装箱和拆箱

深入剖析Java中的装箱和拆箱自动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来一些看一下装箱和拆箱中的若干问题。本文先讲述装箱和拆箱最基本的东西&#x

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

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

  以下是本文的目录大纲:

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

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

  三.面试中相关的问题

  

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

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

Integer i = new Integer(10);

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

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

View Code

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

public static Integer valueOf(int i) {if(i >&#61; -128 && i <&#61; IntegerCache.high)return IntegerCache.cache[i &#43; 128];elsereturn new Integer(i);}

View Code

  而其中IntegerCache类的实现为&#xff1a;

private static class IntegerCache {static final int high;static final Integer cache[];static {final int low &#61; -128;// high value may be configured by propertyint h &#61; 127;if (integerCacheHighPropValue !&#61; null) {// Use Long.decode here to avoid invoking methods that// require Integer&#39;s autoboxing cache to be initializedint i &#61; Long.decode(integerCacheHighPropValue).intValue();i &#61; Math.max(i, 127);// Maximum array size is Integer.MAX_VALUEh &#61; Math.min(i, Integer.MAX_VALUE - -low);}high &#61; h;cache &#61; new Integer[(high - low) &#43; 1];int j &#61; low;for(int k &#61; 0; k )cache[k] &#61; new Integer(j&#43;&#43;);}private IntegerCache() {}}

View Code

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

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

2.下面这段代码的输出结果是什么&#xff1f;

public class Main {public static void main(String[] args) {Double i1 &#61; 100.0;Double i2 &#61; 100.0;Double i3 &#61; 200.0;Double i4 &#61; 200.0;System.out.println(i1&#61;&#61;i2);System.out.println(i3&#61;&#61;i4);}
}

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

false
false

View Code

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

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

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

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

3.下面这段代码输出结果是什么&#xff1a;

public class Main {public static void main(String[] args) {Boolean i1 &#61; false;Boolean i2 &#61; false;Boolean i3 &#61; true;Boolean i4 &#61; true;System.out.println(i1&#61;&#61;i2);System.out.println(i3&#61;&#61;i4);}
}

  输出结果是&#xff1a;

true
true

View Code

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

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

View Code

  而其中的 TRUE 和FALSE又是什么呢&#xff1f;在Boolean中定义了2个静态成员属性&#xff1a;

public static final Boolean TRUE &#61; new Boolean(true);/** * The Boolean object corresponding to the primitive * value false. */public static final Boolean FALSE &#61; new Boolean(false);

View Code

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

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

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

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

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

5.下面程序的输出结果是什么&#xff1f;

public class Main {public static void main(String[] args) {Integer a &#61; 1;Integer b &#61; 2;Integer c &#61; 3;Integer d &#61; 3;Integer e &#61; 321;Integer f &#61; 321;Long g &#61; 3L;Long h &#61; 2L;System.out.println(c&#61;&#61;d);System.out.println(e&#61;&#61;f);System.out.println(c&#61;&#61;(a&#43;b));System.out.println(c.equals(a&#43;b));System.out.println(g&#61;&#61;(a&#43;b));System.out.println(g.equals(a&#43;b));System.out.println(g.equals(a&#43;h));}
}

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

true
false
true
true
true
false
true

View Code

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

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

 

  原文链接&#xff1a;

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


转:https://www.cnblogs.com/jimmy-muyuan/p/5356771.html



推荐阅读
  • 本文总结了Java初学者需要掌握的六大核心知识点,帮助你更好地理解和应用Java编程。无论你是刚刚入门还是希望巩固基础,这些知识点都是必不可少的。 ... [详细]
  • python模块之正则
    re模块可以读懂你写的正则表达式根据你写的表达式去执行任务用re去操作正则正则表达式使用一些规则来检测一些字符串是否符合个人要求,从一段字符串中找到符合要求的内容。在 ... [详细]
  • C#实现文件的压缩与解压
    2019独角兽企业重金招聘Python工程师标准一、准备工作1、下载ICSharpCode.SharpZipLib.dll文件2、项目中引用这个dll二、文件压缩与解压共用类 ... [详细]
  • 本文介绍了 Go 语言中的高性能、可扩展、轻量级 Web 框架 Echo。Echo 框架简单易用,仅需几行代码即可启动一个高性能 HTTP 服务。 ... [详细]
  • 本文将深入探讨 iOS 中的 Grand Central Dispatch (GCD),并介绍如何利用 GCD 进行高效多线程编程。如果你对线程的基本概念还不熟悉,建议先阅读相关基础资料。 ... [详细]
  • 2020年9月15日,Oracle正式发布了最新的JDK 15版本。本次更新带来了许多新特性,包括隐藏类、EdDSA签名算法、模式匹配、记录类、封闭类和文本块等。 ... [详细]
  • 我有一个从C项目编译的.o文件,该文件引用了名为init_static_pool ... [详细]
  • HTTP(HyperTextTransferProtocol)是超文本传输协议的缩写,它用于传送www方式的数据。HTTP协议采用了请求响应模型。客服端向服务器发送一 ... [详细]
  • 本文介绍了如何在 Spring 3.0.5 中使用 JdbcTemplate 插入数据并获取 MySQL 表中的自增主键。 ... [详细]
  • 浅析python实现布隆过滤器及Redis中的缓存穿透原理_python
    本文带你了解了位图的实现,布隆过滤器的原理及Python中的使用,以及布隆过滤器如何应对Redis中的缓存穿透,相信你对布隆过滤 ... [详细]
  • JUC(三):深入解析AQS
    本文详细介绍了Java并发工具包中的核心类AQS(AbstractQueuedSynchronizer),包括其基本概念、数据结构、源码分析及核心方法的实现。 ... [详细]
  • IOS Run loop详解
    为什么80%的码农都做不了架构师?转自http:blog.csdn.netztp800201articledetails9240913感谢作者分享Objecti ... [详细]
  • 本文介绍了在 Java 编程中遇到的一个常见错误:对象无法转换为 long 类型,并提供了详细的解决方案。 ... [详细]
  • 字符串学习时间:1.5W(“W”周,下同)知识点checkliststrlen()函数的返回值是什么类型的?字 ... [详细]
  • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
author-avatar
石pimentel_958
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有