一、首先需要了解的几个前提:

1、自动装箱过程是通过调用valueOf方法实现的(如Integer.valueOf(10)),而拆箱过程是通过调用包装器的 xxxValue方法实现的(如Integer.intValue(a))。

例如代码:

  1. Integer a = 1;
  2. a++;

其自动装箱和拆箱的过程如下:

  1. 0: iconst_1
  2. 1: invokestatic #16 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
  3.  
  4. 4: astore_1
  5. 5: aload_1
  6. 6: invokevirtual #22 // Method java/lang/Integer.intValue:()I
  7. 9: iconst_1
  8. 10: iadd
  9. 11: invokestatic #16 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;

2、缓存

Integer、Short、Byte、Character、Long包装类型有缓存机制(cache数组)。

Boolean类型有TRUE 和 FALSE两个静态成员。

  1. public static final Boolean TRUE = new Boolean(true);
  2.  
  3. /**
  4. * The <code>Boolean</code> object corresponding to the primitive
  5. * value <code>false</code>.
  6. */
  7. public static final Boolean FALSE = new Boolean(false);

Double和Float没有缓存机制。

二、关键的总结写在前面

1、 我们知道,"=="运算符既可以用来比较和基本类型变量和引用类型变量。当两个操作数都是包装器类型的变量时,判定标准为他们是否指向同一个对象;而如果其中有一个操作数是表达式(即包含算术运算)则会先进行自动拆箱,再进行对应基本类型变量比较。

2、基本包装类型重写了equals方法,基本包装类型的equals不会进行类型的转换,类型不同的包装类型的变量直接被判定为false,尽管他们的数值有可能是相等的。

三、从一段代码开始

  1. package com.demo;
  2. public class Interview {
  3. public static void main(String[] args) {
  4. // TODO Auto-generated method stub
  5. int i = 3;
  6. Integer a = 1;
  7. Integer b = 2;
  8. Integer c = 3;
  9. Integer d = 3;
  10. Integer e = 250;
  11. Integer f = 250;
  12. Long g = 3L;
  13. Long h = 2L;
  14.  
  15. System.out.println(i==(a+b));
  16. System.out.println(c==d);
  17. System.out.println(e==f);
  18. System.out.println(c.equals(d));
  19. System.out.println(e.equals(f));
  20. System.out.println(c==(a+b));
  21. System.out.println(c.equals(a+b));
  22. System.out.println(g==(a+b));
  23. System.out.println(g.equals(c));
  24. System.out.println(g.equals(a+b));
  25. System.out.println(g.equals(a+h));
  26. }
  27. }

四、运行结果

  1. true
  2. true
  3. false
  4. true
  5. true
  6. true
  7. true
  8. true
  9. false
  10. false
  11. true

五、后面的解析有点长

将如上代码编译后的class文件进行javap反汇编,基本能够说明大部分的问题。

第一处:

  1. System.out.println(i==(a+b));

执行过程如下:

  1. 61: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  2. 64: aload_3
  3. 65: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  4. 68: iadd
  5. 69: if_icmpne 76

说明分别对a和b进行了自动拆箱操作,然后再add运算,之后在与i进行比较操纵。

第二处:

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

这个大家都知道,在对3和3进行自动装箱成c和d时,直接从Integer的缓存数组cache总取得相同的对象引用。

Integer的valueOf(int i)源码如下:

  1. public static Integer valueOf(int i) {
  2. final int offset = 128;
  3. if (i >= -128 && i <= 127) { // must cache
  4. return IntegerCache.cache[i + offset];
  5. }
  6. return new Integer(i);
  7. }

第三处:

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

与第二处不同的是,自动装箱时250不在-128和127之间,实际上是new了两个不同的对象。

调用了两次如下的Integer的构造器,尽管传入的参数value是都是250,但是确实是不同的对象。

  1. public Integer(int value) {
  2.   this.value = value;
  3. }

第四处和第五处:

  1. System.out.println(c.equals(d));
  2. System.out.println(e.equals(f));

这两处和第二和第三处形成对比,主要的区别是Integer重写了Object的equals方法。

Object的equals源码如下,简单干净,纯粹等同于对象的==比较。

  1. public boolean equals(Object obj) {
  2. return (this == obj);
  3. }

Integer的equals源码如下:

  1. public boolean equals(Object obj) {
  2. if (obj instanceof Integer) {
  3. return value == ((Integer)obj).intValue();
  4. }
  5. return false;
  6. }

可以发现,如果obj如果符合instanceof判定,那么会将obj自动拆箱,实际比较的是两个Integer对象的数值。

这就解释了虽然e和f是不同的对象(==判定为false),但是equals判定为true,因为其数值都为250。

第六处:

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

执行过程如下:

  1. 145: aload 4
  2. 147: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  3. 150: aload_2
  4. 151: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  5. 154: aload_3
  6. 155: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  7. 158: iadd
  8. 159: if_icmpne 166

可以看到对a、b和c都调用Integer.intValue()方法进行自动拆箱。

第七处:

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

执行过程如下:

  1. 173: aload 4
  2. 175: aload_2
  3. 176: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  4. 179: aload_3
  5. 180: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  6. 183: iadd
  7. 184: invokestatic #23 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
  8.  
  9. 187: invokevirtual #52 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z

可以看到其过程为:先对a和b进行自动拆箱,进行add运算后,在对运算结果进行自动装箱,最后调用Integer.equals()方法进行判定。

第八处:

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

其执行结果如下:

  1. 196: aload 8
  2. 198: invokevirtual #55 // Method java/lang/Long.longValue:()J
  3. 201: aload_2
  4. 202: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  5. 205: aload_3
  6. 206: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  7. 209: iadd
  8. 210: i2l
  9. 211: lcmp

与第六处的原理基本类似,都是进行自动拆箱,只不过这里g是调用Long.longValue()方法。

第九处:

  1. System.out.println(g.equals(c));

这个地方应当特别注意,观察Integer的equals源码不难发现,equals判定的第一步是进行instanceof,显然c不是Long的实例,判定失败,直接返回false。

也就是说这里根本没有进行数值比较的机会。

因此,基本包装类型的equals不会进行类型的转换,类型不同的包装类型对象直接被判定为false,尽管他们的数值有可能是相等的。

第十处:

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

执行过程如下:

  1. 239: aload 8
  2. 241: aload_2
  3. 242: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  4. 245: aload_3
  5. 246: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  6. 249: iadd
  7. 250: invokestatic #23 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
  8.  
  9. 253: invokevirtual #59 // Method java/lang/Long.equals:(Ljava/lang/Object;)Z

这其实是第九处的扩展。

不难看出,其执行过程为:先对a和b进行自动拆箱,进行add运算,在对结果进行自动装箱,在进行equals判定。

但是add运算的结果自动装箱后依然是Integer类型,由第九处可知,自然会被判定为false。

第十一处:

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

执行过程如下:

  1. 262: aload 8
  2. 264: aload_2
  3. 265: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  4. 268: i2l
  5. 269: aload 9
  6. 271: invokevirtual #55 // Method java/lang/Long.longValue:()J
  7. 274: ladd
  8. 275: invokestatic #31 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
  9. 278: invokevirtual #59 // Method java/lang/Long.equals:(Ljava/lang/Object;)Z

与第十处不同之处在于,a和h自动拆箱后进行add运算的会向上转型为long类型,在对其自动装箱后自然会被包装成Long类型。

同时,两个比较对象的数值相等,自然会被判定为true。

六、最后附上完整的反编译代码

  1. D:\java\qcworkspace\test\bin\com\demo>javap -c Interview
  2. 警告: 二进制文件Interview包含com.demo.Interview
  3. Compiled from "Interview.java"
  4. public class com.demo.Interview {
  5. public com.demo.Interview();
  6. Code:
  7. 0: aload_0
  8. 1: invokespecial #8 // Method java/lang/Object."<init>":()V
  9. 4: return
  10.  
  11. public static boolean test(java.lang.Object);
  12. Code:
  13. 0: aload_0
  14. 1: instanceof #16 // class java/lang/Long
  15. 4: ifeq 9
  16. 7: iconst_1
  17. 8: ireturn
  18. 9: iconst_0
  19. 10: ireturn
  20.  
  21. public static void main(java.lang.String[]);
  22. Code:
  23. 0: iconst_3
  24. 1: istore_1
  25. 2: iconst_1
  26. 3: invokestatic #23 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
  27.  
  28. 6: astore_2
  29. 7: iconst_2
  30. 8: invokestatic #23 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
  31.  
  32. 11: astore_3
  33. 12: iconst_3
  34. 13: invokestatic #23 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
  35.  
  36. 16: astore 4
  37. 18: iconst_3
  38. 19: invokestatic #23 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
  39.  
  40. 22: astore 5
  41. 24: sipush 250
  42. 27: invokestatic #23 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
  43.  
  44. 30: astore 6
  45. 32: sipush 250
  46. 35: invokestatic #23 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
  47.  
  48. 38: astore 7
  49. 40: ldc2_w #29 // long 3l
  50. 43: invokestatic #31 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
  51. 46: astore 8
  52. 48: ldc2_w #34 // long 2l
  53. 51: invokestatic #31 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
  54. 54: astore 9
  55. 56: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
  56. 59: iload_1
  57. 60: aload_2
  58. 61: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  59. 64: aload_3
  60. 65: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  61. 68: iadd
  62. 69: if_icmpne 76
  63. 72: iconst_1
  64. 73: goto 77
  65. 76: iconst_0
  66. 77: invokevirtual #46 // Method java/io/PrintStream.println:(Z)V
  67. 80: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
  68. 83: aload 4
  69. 85: aload 5
  70. 87: if_acmpne 94
  71. 90: iconst_1
  72. 91: goto 95
  73. 94: iconst_0
  74. 95: invokevirtual #46 // Method java/io/PrintStream.println:(Z)V
  75. 98: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
  76. 101: aload 6
  77. 103: aload 7
  78. 105: if_acmpne 112
  79. 108: iconst_1
  80. 109: goto 113
  81. 112: iconst_0
  82. 113: invokevirtual #46 // Method java/io/PrintStream.println:(Z)V
  83. 116: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
  84. 119: aload 4
  85. 121: aload 5
  86. 123: invokevirtual #52 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
  87. 126: invokevirtual #46 // Method java/io/PrintStream.println:(Z)V
  88. 129: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
  89. 132: aload 6
  90. 134: aload 7
  91. 136: invokevirtual #52 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
  92. 139: invokevirtual #46 // Method java/io/PrintStream.println:(Z)V
  93. 142: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
  94. 145: aload 4
  95. 147: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  96. 150: aload_2
  97. 151: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  98. 154: aload_3
  99. 155: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  100. 158: iadd
  101. 159: if_icmpne 166
  102. 162: iconst_1
  103. 163: goto 167
  104. 166: iconst_0
  105. 167: invokevirtual #46 // Method java/io/PrintStream.println:(Z)V
  106. 170: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
  107. 173: aload 4
  108. 175: aload_2
  109. 176: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  110. 179: aload_3
  111. 180: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  112. 183: iadd
  113. 184: invokestatic #23 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
  114.  
  115. 187: invokevirtual #52 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
  116. 190: invokevirtual #46 // Method java/io/PrintStream.println:(Z)V
  117. 193: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
  118. 196: aload 8
  119. 198: invokevirtual #55 // Method java/lang/Long.longValue:()J
  120. 201: aload_2
  121. 202: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  122. 205: aload_3
  123. 206: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  124. 209: iadd
  125. 210: i2l
  126. 211: lcmp
  127. 212: ifne 219
  128. 215: iconst_1
  129. 216: goto 220
  130. 219: iconst_0
  131. 220: invokevirtual #46 // Method java/io/PrintStream.println:(Z)V
  132. 223: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
  133. 226: aload 8
  134. 228: aload 4
  135. 230: invokevirtual #59 // Method java/lang/Long.equals:(Ljava/lang/Object;)Z
  136. 233: invokevirtual #46 // Method java/io/PrintStream.println:(Z)V
  137. 236: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
  138. 239: aload 8
  139. 241: aload_2
  140. 242: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  141. 245: aload_3
  142. 246: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  143. 249: iadd
  144. 250: invokestatic #23 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
  145.  
  146. 253: invokevirtual #59 // Method java/lang/Long.equals:(Ljava/lang/Object;)Z
  147. 256: invokevirtual #46 // Method java/io/PrintStream.println:(Z)V
  148. 259: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
  149. 262: aload 8
  150. 264: aload_2
  151. 265: invokevirtual #42 // Method java/lang/Integer.intValue:()I
  152. 268: i2l
  153. 269: aload 9
  154. 271: invokevirtual #55 // Method java/lang/Long.longValue:()J
  155. 274: ladd
  156. 275: invokestatic #31 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
  157. 278: invokevirtual #59 // Method java/lang/Long.equals:(Ljava/lang/Object;)Z
  158. 281: invokevirtual #46 // Method java/io/PrintStream.println:(Z)V
  159. 284: return
  160. }

吁。。。。好长。终于完了。

转载注明:http://www.cnblogs.com/qcblog/p/7667058.html

关于java的自动拆装箱若干细节问题的更多相关文章

  1. Java的自动拆/装箱

    作者:Alvin 关键字:语法糖 类 对象 参考 Java 中的语法糖 语法糖--这一篇全了解 浅谈 Integer 类 什么是Java中的自动拆装箱 深入剖析Java中的装箱和拆箱 前言 我们知道, ...

  2. Java之自动拆装箱

    顾名思义,自动拆装箱就是将基本类型和包装类进行自动的互相转换. JDK5.0后,将自动装箱/拆箱引Java中. 自动装箱的过程:每当需要一种类型的对象时,这种基本类型就自动地封装到与它相同类型的包装中 ...

  3. Java的自动拆装箱与Integer的缓存机制

    转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10832303.html 一:基本类型与包装类型     我们知道,Java有8大基本数据类型,4整2浮1符1 ...

  4. java中的自动拆装箱与缓存(Java核心技术阅读笔记)

    最近在读<深入理解java核心技术>,对于里面比较重要的知识点做一个记录! 众所周知,Java是一个面向对象的语言,而java中的基本数据类型却不是面向对象的!为了解决这个问题,Java为 ...

  5. Java基础(二) 基本类型数据类型、包装类及自动拆装箱

    我们知道基本数据类型包括byte, short, int, long, float, double, char, boolean,对应的包装类分别是Byte, Short, Integer, Long ...

  6. 《java入门第一季》之Integer类和自动拆装箱概述

    / * int 仅仅是一个基本类型.int有对应的类类型,那就是Integer.  * 为了对基本数据类型进行更多的操作,更方便的操作,Java就针对每一种基本数据类型提供了对应的类类型--包装类类型 ...

  7. java的数据类型、自动拆装箱、字面量

    java 中的数据类型分为两大类:值类型(基本数据类型)和引用类型(复合数据类型)  值类型分为 1,整数类型(byte,short,int,long) 2,浮点类型(float,double) 3, ...

  8. 一文读懂什么是Java中的自动拆装箱

    基本数据类型 基本类型,或者叫做内置类型,是Java中不同于类(Class)的特殊类型.它们是我们编程中使用最频繁的类型. Java是一种强类型语言,第一次申明变量必须说明数据类型,第一次变量赋值称为 ...

  9. Java枚举、静态导入、自动拆装箱、增强for循环、可变参数

    一.枚举简介 1.什么是枚举? 需要在一定范围内取值,这个值只能是这个范围内中的任意一个 现实场景:交通信号灯,有三种颜色,但是每次只能亮三种颜色里面的任意一个 2.使用一个关键字 enum enum ...

随机推荐

  1. setTimeout,setInterval你不知道的…

    javascript线程解释(setTimeout,setInterval你不知道的事) 标签: javascript引擎任务浏览器functionxmlhttprequest 2011-11-21 ...

  2. HIVE和HBASE区别11

    对于刚接触大数据的用户来说,要想区分Hive与HBase是有一定难度的.本文将尝试从其各自的定义.特点.限制.应用场景等角度来进行分析,以作抛砖引玉之用.  Hive是什么? Apache Hive是 ...

  3. Ubuntu 14.02 cmake升级 失败解决

    错误的提示: CMake Error: Could not find CMAKE_ROOT !!! CMake has most likely not been installed correctly ...

  4. js Web存储方式

    JSON是数据交互中最常用的一种数据格式. 由于各种语言的语法都不同,在传递数据时,可以将自己语言中的数组.对象等转换为JSON字符串> 传递之后,可以讲JSON字符串,在解析为JSON对象. ...

  5. 201421123059 http://www.cnblogs.com/deng201421123059/

    201421123059 http://www.cnblogs.com/deng201421123059/

  6. 团队作业4——第一次项目冲刺(Alpha版本)2017.4.27

    2017.04.27 天气阴沉 小雨. 时间:上午 9:35 ---10:10分 地点:陆大314实验室 会议内容:每天充分利用好大课间的时间,今天对昨天的的细节问题进行了讨论及方法更正.时间不等人这 ...

  7. 团队作业4——第一次项目冲刺(Alpha版本)4.23

    ·本次会议为第二次Scrum Meeting会议~ ·本次会议项目经理召开时间为17:00,在教学楼教室召开,召开时长约30分钟,探讨了昨日任务的进展.查重功能.今日要做的任务以及后续所要开展的工作. ...

  8. 201521123024 《Java程序设计》第1周学习总结

    一.本周章学习总结 1.Java的版本迁移 2.运用eclipse和notepad++编写Java 3.对JDK,JRE,JVM有初步的了解 二.书面作业 1.为什么java程序可以跨平台运行?执行j ...

  9. 201521123112《Java程序设计》第9周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 2. 书面作业 本次PTA作业题集异常 1.常用异常 1.1 截图你的提交结果(出现学号) 1.2 自己以前编写的代 ...

  10. 201521123060 《Java程序设计》第12周学习总结

    1.本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 2.书面作业 将Student对象(属性:int id, String name,int age,double ...