Integer與int的區別

jdk1.5引入了自動裝箱(autoboxing)與自動拆箱(unboxing),這方便了集合類以及一些方法的調用,同時也使初學者對其感到非常之困惑。在此,我們來揭開其神祕的面紗。

首先,需要釐清一些概念:
1、Integer是一個類,用Integer聲明一個變量其是一個對象類型(或者說引用類型);int是基本類型,用int聲明的變量是非對象類型,即不能在其上調用方法。
2、“==”作用於對象上的時候,其比較的是對象的引用本身的值(或者說對象的地址更容易理解),而作用於基本類型的時候比較的就是基本類型的值。

接下來看一段代碼:

public class Test {
    public static void main(String[] args) {
        Integer i1 = 2;
        int i2 = 2;
        System.out.println(i1 == i2);
    }
}

在這段代碼中有兩個令人困惑的問題,首先是將一個基本類型的值賦值給對象的引用,即Integer i1 =2;其次是拿一個對象類型和一個基本類型比較。按理說這兩種做法肯定都是有問題的,在jdk1.4(若使用的jdk版本是1.5或之後的版本中,可以使用javac -source 1.4 Test.java來編譯)上,確實如此,第一個問題在編譯時會報“不兼容的類型”錯誤,第二個問題會報“運算符 == 不能應用於 java.lang.Integer,int”的錯誤。

但是jdk1.5引入的自動裝箱和自動拆箱,那麼,必然要將其中的一種類型轉換成另一種類型,究竟是將Integer對象i1轉換成int基本類型呢?還是將int基本類型的i2轉換成Integer對象?通過javap -c Test反編譯Test.class文件就知道答案了:

public static void main(java.lang.String[]);
  Code:
   0:   iconst_2
   1:   invokestatic    #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   4:   astore_1
   5:   iconst_2
   6:   istore_2
   7:   getstatic       #3; //Field java/lang/System.out:Ljava/io/PrintStream;
   10:  aload_1
   11:  invokevirtual   #4; //Method java/lang/Integer.intValue:()I
   14:  iload_2
   15:  if_icmpne       22
   18:  iconst_1
   19goto    23
   22:  iconst_0
   23:  invokevirtual   #5; //Method java/io/PrintStream.println:(Z)V
   26return
}

其中,[0-4]是Integer i1 = 2的實現,我們發現,編譯的字節碼裏調用了Integer.valueOf方法,因此Integer i1 = 2編譯後就等同於Integer i1 = Integer.valueOf(2);[5,6]是int i2 = 2的實現;[7,23]是System.out.println(i1 == i2)的實現,也容易看到,裏面調用了Integer.intValue()方法。因此,這個i1 == i2這兩個不同類型的變量比較,在編譯的時候,編譯器是將其轉換成相同的類型進行比較的,即將對象類型轉換成基本類型,System.out.println(i1 == i2)就等同於System.out.println(i1.intValue() == i2),前面說了,“==”作用於基本類型的時候比較的就是基本類型的值,兩個值都是2,所以結果是true。

另外一個令人困惑的例子就是:

public class Test {
    public static void main(String[] args) {
        Integer i1 = 127;
        Integer i2 = 127;
        System.out.println(i1 == i2);
 
        Integer i3 = 128;
        Integer i4 = 128;
        System.out.println(i3 == i4);
    }
}

運行後發現,i1==i2的結果爲true,i3==i4的結果爲false?這令不知原因的人頭疼不已。在前面一個例子裏我們已經說過,諸如Integer i1 = 127,在編譯後就等同於Integer i1 = Integer.valueOf(127),既然是調用一個方法來獲得對象,那麼就有必要對valueOf方法一探究竟了。我們看下源碼:

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);
}

到此應該恍然大悟了,IntegerCache緩存了[-128,127]之間的Integer對象,如果valueOf的參數i處於這之間,就返回緩存的對象。否則就new一個新的Integer。前面已經說過,“==”作用於對象上的時候,其比較的是對象的地址,例子中的i1和i2都是從緩存中拿的,當然是同一個對象,i3和i4都是通過new Integer獲得的,當然不是同一個對象了。

類似地,java.lang.Long,java.lang.Short分別緩存了[-128,127]之間的Long和Short對象,java.lang.Byte緩存了所有的對象,java.lang.Character緩存了[0,127]之間的Character對象。java緩存這些對象是爲了性能優化,既然我們已經知道其緩存了這麼些對象,在需要new Integer/Long/…的地方,可改用Integer/Long/Short…#valueOf方法。


但是:

使用Oracle/Sun JDK 6,在server模式下,使用-XX:AutoBoxCacheMax=NNN參數即可將Integer的自動緩存區間設置爲[-128,NNN]。注意區間的下界固定在-128不可配置。
在client模式下該參數無效。這個參數是server模式專有的,在c2_globals.hpp中聲明,默認值是128;不過這個默認值在默認條件下不起作用,要手動設置它的值或者是開啓-XX:+AggressiveOpts參數才起作用。更多信息http://rednaxelafx.iteye.com/blog/680746

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章