天天看點

【轉】有關Java包裝類及其拆箱裝箱的小結

Java包裝類,Wrapper~由于在java中,資料類型總共可分為兩大種,基本資料類型(值類型)和類類型(引用資料類型)。基本類型的資料不是對象,是以對于要将資料類型作為對象來使用的情況,java提供了相對應的包裝類。對于8種資料類型的包裝類分别是: 

int---Integer 

char---Character 

float---Float 

double---Double 

byte---Byte 

short---Short 

long---Long 

boolean--Boolean 

包裝類提供了很多互相轉換的方法,這裡不一一細說,這裡關注的是包裝類的裝箱和拆箱問題。 

所謂裝箱,就是把基本類型用它們相對應的引用類型包起來,使它們可以具有對象的特質,如我們可以把int型包裝成Integer類的對象,或者把double包裝成Double,等等。 

所謂拆箱,就是跟裝箱的方向相反,将Integer及Double這樣的引用類型的對象重新簡化為值類型的資料 

J2SE5.0後提供了自動裝箱與拆箱的功能,此功能事實上是編譯器來幫您的忙,編譯器在編譯時期依您所編寫的方法,決定是否進行裝箱或拆箱動作。 

自動裝箱的過程:每當需要一種類型的對象時,這種基本類型就自動地封裝到與它相同類型的包裝中。 

自動拆箱的過程:每當需要一個值時,被裝箱對象中的值就被自動地提取出來,沒必要再去調用intValue()和doubleValue()方法。 

自動裝箱,隻需将該值賦給一個類型包裝器引用,java會自動建立一個對象。例如: Java代碼  

【轉】有關Java包裝類及其拆箱裝箱的小結
  1. Integer i=100;//沒有通過使用new來顯示建立,java自動完成。  

自動拆箱,隻需将該對象值賦給一個基本類型即可。例如:int j=i; 

Java代碼  

【轉】有關Java包裝類及其拆箱裝箱的小結
  1. int i = 10;  
  2. Integer j =new Integer(i); //手動裝箱操作    
  3. int k = j.intValue();      //手動拆箱操作    
  4. int i = 11;  
  5. Integer j = i; //自動裝箱  
  6. int k = j //自動拆箱  

然而在Integer的自動裝拆箱會有些細節值得注意: 

Java代碼  

【轉】有關Java包裝類及其拆箱裝箱的小結
  1. public static void main(String[] args) {  
  2.     Integer a=100;  
  3.         Integer b=100;  
  4.         Integer c=200;  
  5.         Integer d=200;  
  6.         System.out.println(a==b);   //1  
  7.         System.out.println(a==100); //2  
  8.         System.out.println(c==d);   //3  
  9.         System.out.println(c==200); //4  
  10.    }  

在java種,"=="是比較object的reference而不是value,自動裝箱後,abcd都是Integer這個Oject,是以“==”比較的是其引用。按照正常思維,1和3都應該輸出false。但結果是: 

true 

true 

false 

true 

結果2和4,是因為ac進行了自動拆箱,是以其比較是基本資料類型的比較,就跟int比較時一樣的,“==”在這裡比較的是它們的值,而不是引用。 

對于結果1,雖然比較的時候,還是比較對象的reference,但是自動裝箱時,java在編譯的時候 Integer a = 100; 被翻譯成-> Integer a = Integer.valueOf(100); 

關鍵就在于這個valueOf()的方法。 

Java代碼  

【轉】有關Java包裝類及其拆箱裝箱的小結
  1. public static Integer valueOf(int i) {     
  2. final int offset = 128;     
  3. if (i >= -128 && i <= 127) { // must cache     
  4. return IntegerCache.cache[i + offset];     
  5. }     
  6. return new Integer(i);     
  7. }     

Java代碼  

【轉】有關Java包裝類及其拆箱裝箱的小結
  1. private static class IntegerCache {     
  2. private IntegerCache(){}     
  3. static final Integer cache[] = new Integer[-(-128) + 127 + 1];     
  4. static {     
  5. for(int i = 0; i < cache.length; i++)     
  6. cache = new Integer(i - 128);     
  7. }     
  8. }   

根據上面的jdk源碼,java為了提高效率,IntegerCache類中有一個數組緩存了值從-128到127的Integer對象。當我們調用Integer.valueOf(int i)的時候,如果i的值是>=-128且<=127時,會直接從這個緩存中傳回一個對象,否則就new一個Integer對象。  

具體如下: 

Java代碼  

【轉】有關Java包裝類及其拆箱裝箱的小結
  1. static final Integer cache[] = new Integer[-(-128) + 127 + 1]; //将cache[]變成靜态  
  2. static {  
  3.         for(int i = 0; i < cache.length; i++)  
  4.         cache[i] = new Integer(i - 128); //初始化cache[i]  
  5.     }  

這是用一個for循環對數組cache指派,cache[255] = new Integer(255-128),也就是newl一個Integer(127) ,并把引用指派給cache[255],好了,然後是Integer b= 127,流程基本一樣,最後又到了cache[255] = new Integer(255-128),這一句,那我們迷糊了,這不是又new了一個對象127嗎,然後把引用指派給cache[255],我們比較這兩個引用(前面聲明a的時候也有一個),由于是不同的位址,是以肯定不會相等,應該傳回false啊!呵呵,這麼想你就錯了,請注意看for語句給cache[i]初始化的時候外面還一個{}呢,{}前面一個大大的static關鍵字,是靜态的,那麼我們就可以回想下static有什麼特性了,隻能初始化一次,在對象間共享,也就是不同的對象共享同一個static資料。 

那麼當我們Integer b = 127的時候,并沒有new出一個新對象來,而是共享了a這個對象的引用,記住,他們共享了同一個引用!!!,那麼我們進行比較a==b時,由于是同一個對象的引用(她們在堆中的位址相同),那當然傳回true了!!! 

知道了為什麼,我們就來看看下面的代碼: 

Java代碼  

【轉】有關Java包裝類及其拆箱裝箱的小結
  1. public class Test {  
  2.         public static void main(String args[]){  
  3.                 Integer m = new Integer(5);  
  4.                 Integer n = new Integer(5);  
  5.                 System.out.println(m==n);  
  6.                 m = m-1;  
  7.                 n = n-1;  
  8.                 System.out.println(m==n);  
  9.         }  
  10. }  

輸出什麼? 

false 

true 

原因: 

m,n因為都是new出來的對象,記憶體位址不一樣,是以第一次m==n比較的reference不一樣。但是,m=m-1首先進行了自動拆箱m.intValue,相減後再進行裝箱動作:m=Integer.valueOf(m.intValue-1),而m和n都在 -128--127的範圍,是以自動裝箱後,根據上文所述,都是同一個object的引用。是以第二次輸出true。 

再看一次(出自java解惑) 

Java代碼  

【轉】有關Java包裝類及其拆箱裝箱的小結
  1. public class TestWhile{ //1  
  2.   public static void main(String[] args)//2  
  3.   {  
  4.   Integer i=0;//3  
  5.   Integer j=0;//4  
  6.   // Integer i=new Integer(0);//5  
  7.   // Integer j=new Integer(0);//6  
  8.   while(i<=j & i>=j & i!=j)//7  
  9.   { //8  
  10.   System.out.println("0000");//9  
  11.   }  
  12.   }  
  13. }  

那一行是拆箱?while循環裡的條件看似不成立,可為什麼可以運作(去掉第5、6行的注釋後)? 

解答: 

這種情況下,循環不能運作。 

對于Integer類型,<,<=,>,>=操作将導緻拆箱操作,也就是調用Integer的intValue()方法得到相應的基本類型值,然後比較。 

但是,==,!=比較的,是對象的引用(Reference)。 

Integer i = 0;//3 

Integer j = 0;//4 

這兩句使用裝箱操作,也就是調用Integer.valueOf(int i);注意,不是使用new。 

由于0在 -128--127之間,根據上述,i和j引用的是同一個對象。  

綜上,i<=j & i>=j & i!=j中, 

i<=j 和 i>=j都成立,而i!=j不成立,因為i和j引用的是同一個對象。 

故此,循環不會執行。 

注釋掉3,4兩句,使用5,6兩句時,i和j引用的不是同一個對象,是以i!=j成立。i<=j & i>=j & i!=j成立,循環條件總是成立的,while (i <= j & i >= j & i != j)成為無窮循環,不斷輸出。 

原題: 

循環者的詛咒 

請提供一個對i的聲明,将下面的循環轉變為一個無限循環:  

while (i <= j && j <= i && i != j) { 

總結,對于要比較Object的值,最穩妥的方法還是調用equals()這個方法,而不是使用==,是以會比較其引用。 

Java代碼  

【轉】有關Java包裝類及其拆箱裝箱的小結
  1. public boolean equals(Object obj) {  
  2.         if (obj instanceof Integer) {  
  3.             return value == ((Integer)obj).intValue();  
  4.         }  
  5.         return false;  
  6. }  

大家可以看到,隻要兩個Integer的int value是相等的,equals方法總是傳回true。