关于java的自动拆装箱若干细节问题
一、首先需要了解的几个前提:
1、自动装箱过程是通过调用valueOf方法实现的(如Integer.valueOf(10)),而拆箱过程是通过调用包装器的 xxxValue方法实现的(如Integer.intValue(a))。
例如代码:
- Integer a = 1;
- a++;
其自动装箱和拆箱的过程如下:
- 0: iconst_1
- 1: invokestatic #16 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 4: astore_1
- 5: aload_1
- 6: invokevirtual #22 // Method java/lang/Integer.intValue:()I
- 9: iconst_1
- 10: iadd
- 11: invokestatic #16 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
2、缓存
Integer、Short、Byte、Character、Long包装类型有缓存机制(cache数组)。
Boolean类型有TRUE 和 FALSE两个静态成员。
- 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);
Double和Float没有缓存机制。
二、关键的总结写在前面
1、 我们知道,"=="运算符既可以用来比较和基本类型变量和引用类型变量。当两个操作数都是包装器类型的变量时,判定标准为他们是否指向同一个对象;而如果其中有一个操作数是表达式(即包含算术运算)则会先进行自动拆箱,再进行对应基本类型变量比较。
2、基本包装类型重写了equals方法,基本包装类型的equals不会进行类型的转换,类型不同的包装类型的变量直接被判定为false,尽管他们的数值有可能是相等的。
三、从一段代码开始
- package com.demo;
- public class Interview {
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- int i = 3;
- Integer a = 1;
- Integer b = 2;
- Integer c = 3;
- Integer d = 3;
- Integer e = 250;
- Integer f = 250;
- Long g = 3L;
- Long h = 2L;
- System.out.println(i==(a+b));
- System.out.println(c==d);
- System.out.println(e==f);
- System.out.println(c.equals(d));
- System.out.println(e.equals(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(c));
- System.out.println(g.equals(a+b));
- System.out.println(g.equals(a+h));
- }
- }
四、运行结果
- true
- true
- false
- true
- true
- true
- true
- true
- false
- false
- true
五、后面的解析有点长
将如上代码编译后的class文件进行javap反汇编,基本能够说明大部分的问题。
第一处:
- System.out.println(i==(a+b));
执行过程如下:
- 61: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 64: aload_3
- 65: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 68: iadd
- 69: if_icmpne 76
说明分别对a和b进行了自动拆箱操作,然后再add运算,之后在与i进行比较操纵。
第二处:
- System.out.println(c==d);
这个大家都知道,在对3和3进行自动装箱成c和d时,直接从Integer的缓存数组cache总取得相同的对象引用。
Integer的valueOf(int i)源码如下:
- public static Integer valueOf(int i) {
- final int offset = 128;
- if (i >= -128 && i <= 127) { // must cache
- return IntegerCache.cache[i + offset];
- }
- return new Integer(i);
- }
第三处:
- System.out.println(e==f);
与第二处不同的是,自动装箱时250不在-128和127之间,实际上是new了两个不同的对象。
调用了两次如下的Integer的构造器,尽管传入的参数value是都是250,但是确实是不同的对象。
- public Integer(int value) {
- this.value = value;
- }
第四处和第五处:
- System.out.println(c.equals(d));
- System.out.println(e.equals(f));
这两处和第二和第三处形成对比,主要的区别是Integer重写了Object的equals方法。
Object的equals源码如下,简单干净,纯粹等同于对象的==比较。
- public boolean equals(Object obj) {
- return (this == obj);
- }
Integer的equals源码如下:
- public boolean equals(Object obj) {
- if (obj instanceof Integer) {
- return value == ((Integer)obj).intValue();
- }
- return false;
- }
可以发现,如果obj如果符合instanceof判定,那么会将obj自动拆箱,实际比较的是两个Integer对象的数值。
这就解释了虽然e和f是不同的对象(==判定为false),但是equals判定为true,因为其数值都为250。
第六处:
- System.out.println(c==(a+b));
执行过程如下:
- 145: aload 4
- 147: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 150: aload_2
- 151: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 154: aload_3
- 155: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 158: iadd
- 159: if_icmpne 166
可以看到对a、b和c都调用Integer.intValue()方法进行自动拆箱。
第七处:
- System.out.println(c.equals(a+b));
执行过程如下:
- 173: aload 4
- 175: aload_2
- 176: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 179: aload_3
- 180: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 183: iadd
- 184: invokestatic #23 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 187: invokevirtual #52 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
可以看到其过程为:先对a和b进行自动拆箱,进行add运算后,在对运算结果进行自动装箱,最后调用Integer.equals()方法进行判定。
第八处:
- System.out.println(g==(a+b));
其执行结果如下:
- 196: aload 8
- 198: invokevirtual #55 // Method java/lang/Long.longValue:()J
- 201: aload_2
- 202: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 205: aload_3
- 206: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 209: iadd
- 210: i2l
- 211: lcmp
与第六处的原理基本类似,都是进行自动拆箱,只不过这里g是调用Long.longValue()方法。
第九处:
- System.out.println(g.equals(c));
这个地方应当特别注意,观察Integer的equals源码不难发现,equals判定的第一步是进行instanceof,显然c不是Long的实例,判定失败,直接返回false。
也就是说这里根本没有进行数值比较的机会。
因此,基本包装类型的equals不会进行类型的转换,类型不同的包装类型对象直接被判定为false,尽管他们的数值有可能是相等的。
第十处:
- System.out.println(g.equals(a+b));
执行过程如下:
- 239: aload 8
- 241: aload_2
- 242: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 245: aload_3
- 246: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 249: iadd
- 250: invokestatic #23 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 253: invokevirtual #59 // Method java/lang/Long.equals:(Ljava/lang/Object;)Z
这其实是第九处的扩展。
不难看出,其执行过程为:先对a和b进行自动拆箱,进行add运算,在对结果进行自动装箱,在进行equals判定。
但是add运算的结果自动装箱后依然是Integer类型,由第九处可知,自然会被判定为false。
第十一处:
- System.out.println(g.equals(a+h));
执行过程如下:
- 262: aload 8
- 264: aload_2
- 265: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 268: i2l
- 269: aload 9
- 271: invokevirtual #55 // Method java/lang/Long.longValue:()J
- 274: ladd
- 275: invokestatic #31 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
- 278: invokevirtual #59 // Method java/lang/Long.equals:(Ljava/lang/Object;)Z
与第十处不同之处在于,a和h自动拆箱后进行add运算的会向上转型为long类型,在对其自动装箱后自然会被包装成Long类型。
同时,两个比较对象的数值相等,自然会被判定为true。
六、最后附上完整的反编译代码
- D:\java\qcworkspace\test\bin\com\demo>javap -c Interview
- 警告: 二进制文件Interview包含com.demo.Interview
- Compiled from "Interview.java"
- public class com.demo.Interview {
- public com.demo.Interview();
- Code:
- 0: aload_0
- 1: invokespecial #8 // Method java/lang/Object."<init>":()V
- 4: return
- public static boolean test(java.lang.Object);
- Code:
- 0: aload_0
- 1: instanceof #16 // class java/lang/Long
- 4: ifeq 9
- 7: iconst_1
- 8: ireturn
- 9: iconst_0
- 10: ireturn
- public static void main(java.lang.String[]);
- Code:
- 0: iconst_3
- 1: istore_1
- 2: iconst_1
- 3: invokestatic #23 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 6: astore_2
- 7: iconst_2
- 8: invokestatic #23 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 11: astore_3
- 12: iconst_3
- 13: invokestatic #23 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 16: astore 4
- 18: iconst_3
- 19: invokestatic #23 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 22: astore 5
- 24: sipush 250
- 27: invokestatic #23 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 30: astore 6
- 32: sipush 250
- 35: invokestatic #23 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 38: astore 7
- 40: ldc2_w #29 // long 3l
- 43: invokestatic #31 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
- 46: astore 8
- 48: ldc2_w #34 // long 2l
- 51: invokestatic #31 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
- 54: astore 9
- 56: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
- 59: iload_1
- 60: aload_2
- 61: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 64: aload_3
- 65: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 68: iadd
- 69: if_icmpne 76
- 72: iconst_1
- 73: goto 77
- 76: iconst_0
- 77: invokevirtual #46 // Method java/io/PrintStream.println:(Z)V
- 80: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
- 83: aload 4
- 85: aload 5
- 87: if_acmpne 94
- 90: iconst_1
- 91: goto 95
- 94: iconst_0
- 95: invokevirtual #46 // Method java/io/PrintStream.println:(Z)V
- 98: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
- 101: aload 6
- 103: aload 7
- 105: if_acmpne 112
- 108: iconst_1
- 109: goto 113
- 112: iconst_0
- 113: invokevirtual #46 // Method java/io/PrintStream.println:(Z)V
- 116: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
- 119: aload 4
- 121: aload 5
- 123: invokevirtual #52 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
- 126: invokevirtual #46 // Method java/io/PrintStream.println:(Z)V
- 129: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
- 132: aload 6
- 134: aload 7
- 136: invokevirtual #52 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
- 139: invokevirtual #46 // Method java/io/PrintStream.println:(Z)V
- 142: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
- 145: aload 4
- 147: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 150: aload_2
- 151: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 154: aload_3
- 155: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 158: iadd
- 159: if_icmpne 166
- 162: iconst_1
- 163: goto 167
- 166: iconst_0
- 167: invokevirtual #46 // Method java/io/PrintStream.println:(Z)V
- 170: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
- 173: aload 4
- 175: aload_2
- 176: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 179: aload_3
- 180: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 183: iadd
- 184: invokestatic #23 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 187: invokevirtual #52 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
- 190: invokevirtual #46 // Method java/io/PrintStream.println:(Z)V
- 193: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
- 196: aload 8
- 198: invokevirtual #55 // Method java/lang/Long.longValue:()J
- 201: aload_2
- 202: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 205: aload_3
- 206: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 209: iadd
- 210: i2l
- 211: lcmp
- 212: ifne 219
- 215: iconst_1
- 216: goto 220
- 219: iconst_0
- 220: invokevirtual #46 // Method java/io/PrintStream.println:(Z)V
- 223: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
- 226: aload 8
- 228: aload 4
- 230: invokevirtual #59 // Method java/lang/Long.equals:(Ljava/lang/Object;)Z
- 233: invokevirtual #46 // Method java/io/PrintStream.println:(Z)V
- 236: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
- 239: aload 8
- 241: aload_2
- 242: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 245: aload_3
- 246: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 249: iadd
- 250: invokestatic #23 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 253: invokevirtual #59 // Method java/lang/Long.equals:(Ljava/lang/Object;)Z
- 256: invokevirtual #46 // Method java/io/PrintStream.println:(Z)V
- 259: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
- 262: aload 8
- 264: aload_2
- 265: invokevirtual #42 // Method java/lang/Integer.intValue:()I
- 268: i2l
- 269: aload 9
- 271: invokevirtual #55 // Method java/lang/Long.longValue:()J
- 274: ladd
- 275: invokestatic #31 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
- 278: invokevirtual #59 // Method java/lang/Long.equals:(Ljava/lang/Object;)Z
- 281: invokevirtual #46 // Method java/io/PrintStream.println:(Z)V
- 284: return
- }
吁。。。。好长。终于完了。
转载注明:http://www.cnblogs.com/qcblog/p/7667058.html
关于java的自动拆装箱若干细节问题的更多相关文章
- Java的自动拆/装箱
作者:Alvin 关键字:语法糖 类 对象 参考 Java 中的语法糖 语法糖--这一篇全了解 浅谈 Integer 类 什么是Java中的自动拆装箱 深入剖析Java中的装箱和拆箱 前言 我们知道, ...
- Java之自动拆装箱
顾名思义,自动拆装箱就是将基本类型和包装类进行自动的互相转换. JDK5.0后,将自动装箱/拆箱引Java中. 自动装箱的过程:每当需要一种类型的对象时,这种基本类型就自动地封装到与它相同类型的包装中 ...
- Java的自动拆装箱与Integer的缓存机制
转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10832303.html 一:基本类型与包装类型 我们知道,Java有8大基本数据类型,4整2浮1符1 ...
- java中的自动拆装箱与缓存(Java核心技术阅读笔记)
最近在读<深入理解java核心技术>,对于里面比较重要的知识点做一个记录! 众所周知,Java是一个面向对象的语言,而java中的基本数据类型却不是面向对象的!为了解决这个问题,Java为 ...
- Java基础(二) 基本类型数据类型、包装类及自动拆装箱
我们知道基本数据类型包括byte, short, int, long, float, double, char, boolean,对应的包装类分别是Byte, Short, Integer, Long ...
- 《java入门第一季》之Integer类和自动拆装箱概述
/ * int 仅仅是一个基本类型.int有对应的类类型,那就是Integer. * 为了对基本数据类型进行更多的操作,更方便的操作,Java就针对每一种基本数据类型提供了对应的类类型--包装类类型 ...
- java的数据类型、自动拆装箱、字面量
java 中的数据类型分为两大类:值类型(基本数据类型)和引用类型(复合数据类型) 值类型分为 1,整数类型(byte,short,int,long) 2,浮点类型(float,double) 3, ...
- 一文读懂什么是Java中的自动拆装箱
基本数据类型 基本类型,或者叫做内置类型,是Java中不同于类(Class)的特殊类型.它们是我们编程中使用最频繁的类型. Java是一种强类型语言,第一次申明变量必须说明数据类型,第一次变量赋值称为 ...
- Java枚举、静态导入、自动拆装箱、增强for循环、可变参数
一.枚举简介 1.什么是枚举? 需要在一定范围内取值,这个值只能是这个范围内中的任意一个 现实场景:交通信号灯,有三种颜色,但是每次只能亮三种颜色里面的任意一个 2.使用一个关键字 enum enum ...
随机推荐
- setTimeout,setInterval你不知道的…
javascript线程解释(setTimeout,setInterval你不知道的事) 标签: javascript引擎任务浏览器functionxmlhttprequest 2011-11-21 ...
- HIVE和HBASE区别11
对于刚接触大数据的用户来说,要想区分Hive与HBase是有一定难度的.本文将尝试从其各自的定义.特点.限制.应用场景等角度来进行分析,以作抛砖引玉之用. Hive是什么? Apache Hive是 ...
- Ubuntu 14.02 cmake升级 失败解决
错误的提示: CMake Error: Could not find CMAKE_ROOT !!! CMake has most likely not been installed correctly ...
- js Web存储方式
JSON是数据交互中最常用的一种数据格式. 由于各种语言的语法都不同,在传递数据时,可以将自己语言中的数组.对象等转换为JSON字符串> 传递之后,可以讲JSON字符串,在解析为JSON对象. ...
- 201421123059 http://www.cnblogs.com/deng201421123059/
201421123059 http://www.cnblogs.com/deng201421123059/
- 团队作业4——第一次项目冲刺(Alpha版本)2017.4.27
2017.04.27 天气阴沉 小雨. 时间:上午 9:35 ---10:10分 地点:陆大314实验室 会议内容:每天充分利用好大课间的时间,今天对昨天的的细节问题进行了讨论及方法更正.时间不等人这 ...
- 团队作业4——第一次项目冲刺(Alpha版本)4.23
·本次会议为第二次Scrum Meeting会议~ ·本次会议项目经理召开时间为17:00,在教学楼教室召开,召开时长约30分钟,探讨了昨日任务的进展.查重功能.今日要做的任务以及后续所要开展的工作. ...
- 201521123024 《Java程序设计》第1周学习总结
一.本周章学习总结 1.Java的版本迁移 2.运用eclipse和notepad++编写Java 3.对JDK,JRE,JVM有初步的了解 二.书面作业 1.为什么java程序可以跨平台运行?执行j ...
- 201521123112《Java程序设计》第9周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 2. 书面作业 本次PTA作业题集异常 1.常用异常 1.1 截图你的提交结果(出现学号) 1.2 自己以前编写的代 ...
- 201521123060 《Java程序设计》第12周学习总结
1.本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 2.书面作业 将Student对象(属性:int id, String name,int age,double ...