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 19 :
goto
23 22 :
iconst_0 23 :
invokevirtual # 5 ;
//Method
java/io/PrintStream.println:(Z)V 26 :
return } |
其中,[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