一、首先需要了解的幾個前提
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 }
完!
轉載請注明原文出處: