关于java的自动拆装箱若干细节问题
Posted Qcer
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于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中取得相同的对象引用,因此==判定返回true。
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,但是确实是不同的对象。因此==判定返回false。
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()方法进行自动拆箱,最终比较的仍然是数值,因此返回true。
第七处:
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()方法进行判定,而equals判定最终比较的还是数组,因此返回true。
第八处:
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 void main(java.lang.String[]); Code: 0: iconst_3 1: istore_1 2: iconst_1 3: invokestatic #16 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 6: astore_2 7: iconst_2 8: invokestatic #16 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 11: astore_3 12: iconst_3 13: invokestatic #16 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 16: astore 4 18: iconst_3 19: invokestatic #16 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 22: astore 5 24: sipush 250 27: invokestatic #16 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 30: astore 6 32: sipush 250 35: invokestatic #16 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 38: astore 7 40: ldc2_w #22 // long 3l 43: invokestatic #24 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long; 46: astore 8 48: ldc2_w #29 // long 2l 51: invokestatic #24 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long; 54: astore 9 56: getstatic #31 // Field java/lang/System.out:Ljava/io/PrintStream; 59: iload_1 60: aload_2 61: invokevirtual #37 // Method java/lang/Integer.intValue:()I 64: aload_3 65: invokevirtual #37 // Method java/lang/Integer.intValue:()I 68: iadd 69: if_icmpne 76 72: iconst_1 73: goto 77 76: iconst_0 77: invokevirtual #41 // Method java/io/PrintStream.println:(Z)V 80: getstatic #31 // 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 #41 // Method java/io/PrintStream.println:(Z)V 98: getstatic #31 // 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 #41 // Method java/io/PrintStream.println:(Z)V 116: getstatic #31 // Field java/lang/System.out:Ljava/io/PrintStream; 119: aload 4 121: aload 5 123: invokevirtual #47 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z 126: invokevirtual #41 // Method java/io/PrintStream.println:(Z)V 129: getstatic #31 // Field java/lang/System.out:Ljava/io/PrintStream; 132: aload 6 134: aload 7 136: invokevirtual #47 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z 139: invokevirtual #41 // Method java/io/PrintStream.println:(Z)V 142: getstatic #31 // Field java/lang/System.out:Ljava/io/PrintStream; 145: aload 4 147: invokevirtual #37 // Method java/lang/Integer.intValue:()I 150: aload_2 151: invokevirtual #37 // Method java/lang/Integer.intValue:()I 154: aload_3 155: invokevirtual #37 // Method java/lang/Integer.intValue:()I 158: iadd 159: if_icmpne 166 162: iconst_1 163: goto 167 166: iconst_0 167: invokevirtual #41 // Method java/io/PrintStream.println:(Z)V 170: getstatic #31 // Field java/lang/System.out:Ljava/io/PrintStream; 173: aload 4 175: aload_2 176: invokevirtual #37 // Method java/lang/Integer.intValue:()I 179: aload_3 180: invokevirtual #37 // Method java/lang/Integer.intValue:()I 183: iadd 184: invokestatic #16 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 187: invokevirtual #47 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z 190: invokevirtual #41 // Method java/io/PrintStream.println:(Z)V 193: getstatic #31 // Field java/lang/System.out:Ljava/io/PrintStream; 196: aload 8 198: invokevirtual #51 // Method java/lang/Long.longValue:()J 201: aload_2 202: invokevirtual #37 // Method java/lang/Integer.intValue:()I 205: aload_3 206: invokevirtual #37 // 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 #41 // Method java/io/PrintStream.println:(Z)V 223: getstatic #31 // Field java/lang/System.out:Ljava/io/PrintStream; 226: aload 8 228: aload 4 230: invokevirtual #55 // Method java/lang/Long.equals:(Ljava/lang/Object;)Z 233: invokevirtual #41 // Method java/io/PrintStream.println:(Z)V 236: getstatic #31 // Field java/lang/System.out:Ljava/io/PrintStream; 239: aload 8 241: aload_2 242: invokevirtual #37 // Method java/lang/Integer.intValue:()I 245: aload_3 246: invokevirtual #37 // Method java/lang/Integer.intValue:()I 249: iadd 250: invokestatic #16 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 253: invokevirtual #55 // Method java/lang/Long.equals:(Ljava/lang/Object;)Z 256: invokevirtual #41 // Method java/io/PrintStream.println:(Z)V 259: getstatic #31 // Field java/lang/System.out:Ljava/io/PrintStream; 262: aload 8 264: aload_2 265: invokevirtual #37 // Method java/lang/Integer.intValue:()I 268: i2l 269: aload 9 271: invokevirtual #51 // Method java/lang/Long.longValue:()J 274: ladd 275: invokestatic #24 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long; 278: invokevirtual #55 // Method java/lang/Long.equals:(Ljava/lang/Object;)Z 281: invokevirtual #41 // Method java/io/PrintStream.println:(Z)V 284: return }
完!
转载请注明原文出处:
以上是关于关于java的自动拆装箱若干细节问题的主要内容,如果未能解决你的问题,请参考以下文章