1. 程式人生 > >關於java的自動拆裝箱若幹細節問題

關於java的自動拆裝箱若幹細節問題

pack 參數 prim gets 調用 store 成對 rac icon

一、首先需要了解的幾個前提:

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
View Code

五、後面的解析有點長

將如上代碼編譯後的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
}
View Code

籲。。。。好長。終於完了。

轉載註明:https://i.cnblogs.com/EditPosts.aspx?postid=7670159

關於java的自動拆裝箱若幹細節問題