5分钟彻底理解-Java自动装箱、拆箱

什么是自动装箱,拆箱

先抛出定义,Java中基础数据类型与它们的包装类进行运算时,编译器会自动帮我们进行转换,转换过程对程序员是透明的,这就是装箱和拆箱,装箱和拆箱可以让我们的代码更简洁易懂

Java中基础数据类型与它们对应的包装类见下表(共8种):

原始类型 包装类型
boolean Boolean
byte Byte
char Character
float Float
int Integer
long Long
short Short
double Double

当表格中左边列出的基础类型与它们的包装类有如下几种情况时,编译器会自动帮我们进行装箱或拆箱.

  • 进行 = 赋值操作(装箱或拆箱)
  • 进行+,-,*,/混合运算 (拆箱)
  • 进行>,<,==比较运算(拆箱)
  • 调用equals进行比较(装箱)
  • ArrayList,HashMap等集合类 添加基础类型数据时(装箱)

我们看一段平常很常见的代码

public void testAutoBox() {
    List<Float> list = new ArrayList<>();
    list.add(1.0f);
    float firstElement = list.get(0);
}

list集合存储的是Float包装类型,我传入的是float基础类型,所以需要进行装箱,而最后的get方法返回的是Float包装类型,我们赋值给float基础类型,所以需要进行拆箱,很简单,安排的明明白白

具体自动装箱,拆箱,代码是如何实现的

既然编译器帮我们自动进行了装箱,拆箱,那么编译器到底做了些什么,要搞清楚这些,最简单直接的方式就是看类经过编译器编译后的字节码,下面是上面一段代码的字节码实现

  public testAutoBox()V
   L0
    LINENUMBER 15 L0
    NEW java/util/ArrayList
    DUP
    INVOKESPECIAL java/util/ArrayList.<init> ()V
    ASTORE 1
   L1
    LINENUMBER 16 L1
    ALOAD 1
    FCONST_1
    INVOKESTATIC java/lang/Float.valueOf (F)Ljava/lang/Float;
    INVOKEINTERFACE java/util/List.add (Ljava/lang/Object;)Z
    POP
   L2
    LINENUMBER 17 L2
    ALOAD 1
    ICONST_0
    INVOKEINTERFACE java/util/List.get (I)Ljava/lang/Object;
    CHECKCAST java/lang/Float
    INVOKEVIRTUAL java/lang/Float.floatValue ()F
    FSTORE 2
   L3
    LINENUMBER 18 L3
    RETURN
  • L0,对应我们代码的第一行,new了一个ArrayList,并赋值给了1号引用(就是list)。
  • L1,先加载list到栈顶,然后FCONST_1指令就是从常量池加载1.0f浮点数并压入栈顶(这一块知识,见附录1),然后调用了Float类的静态 valueOf方法,进行装箱 ,然后调用list的add方法。
  • L2,先加载list到栈顶,从常量池获取0(float,int,long,double等基础类型初始值都是0),调用list的get方法,检查是否能转换,调用了Float的floatValue方法,进行拆箱,存储得到的浮点数。

所以结果很明显了,以float和Float为例,装箱就是调用Float的valueOf方法new一个Float并赋值,拆箱就是调用Float对象的floatValue方法并赋值返回给float。其他基础类型都是大同小异的,具体可以查看源码。

自动装箱、拆箱中的坑

面试题中经常会有考点就是考察面试者对Java中自动装箱、拆箱是否了解透彻,比如下面这一道面试题?

public void testAutoBox2() {
     //1
     int a = 100;
     Integer b = 100;
     System.out.println(a == b);
     
     //2
     Integer c = 100;
     Integer d = 100;
     System.out.println(c == d);
     
     //3   
     c = 200;
     d = 200;
     System.out.println(c == d);
}

<font color="#dd0000">请问执行结果是多少?</font>

题目很常见啦,客官别见笑,我们来分析一下,

  • 第1段代码,基础类型a与包装类b进行==比较,这时b会拆箱,直接比较值,所以会打印true
  • 第2段代码,二个包装类型,都被赋值了100,所以根据我们之前的解析,这时会进行装箱,调用Integer的valueOf方法,生成2个Integer对象,引用类型==比较,直接比较对象指针,这里我们先给出结论,最后会分析原因,打印 true
  • 跟上面第2段代码类似,只不过赋值变成了200,直接说结论,打印 false
运行结果.png

结果是不是很诡异,我们直接去看Integer类valueOf方法的实现(JDK8的实现)

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

可以看到,这里的实现并不是简单的new Integer,而是用IntegerCache做一个cache,cache的range是可以配置的

private static class IntegerCache {
  static final int low = -128;
  static final int high;
  static final Integer cache[];

  static {
  int h = 127;
  String integerCacheHighPropValue =
          sun.misc.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++);
  ....

这是IntegerCache静态代码块中的一段,默认Integer cache 的下限是-128,上限默认127,可以配置,所以到这里就清楚了,我们上面当赋值100给Integer时,刚好在这个range内,所以从cache中取对应的Integer并返回,所以二次返回的是同一个对象,所以==比较是相等的,当赋值200给Integer时,不在cache 的范围内,所以会new Integer并返回,当然==比较的结果是不相等的。

以上

附录1:JVM字节码整型的入栈指令有4个,分别是:

  • iconst(0~5分别对应iconst_0、iconst_1、iconst_2、iconst_3、iconst_4、iconst_5,-1对应iconst_m1)
  • bipush (-128~127)
  • sipush (-32768~32767)
  • ldc (-2147483648~2147483647)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容