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代碼
- Integer i=100;//沒有通過使用new來顯示建立,java自動完成。
自動拆箱,隻需将該對象值賦給一個基本類型即可。例如:int j=i;
Java代碼
- int i = 10;
- Integer j =new Integer(i); //手動裝箱操作
- int k = j.intValue(); //手動拆箱操作
- int i = 11;
- Integer j = i; //自動裝箱
- int k = j //自動拆箱
然而在Integer的自動裝拆箱會有些細節值得注意:
Java代碼
- public static void main(String[] args) {
- Integer a=100;
- Integer b=100;
- Integer c=200;
- Integer d=200;
- System.out.println(a==b); //1
- System.out.println(a==100); //2
- System.out.println(c==d); //3
- System.out.println(c==200); //4
- }
在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代碼
- 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);
- }
Java代碼
- private static class IntegerCache {
- private IntegerCache(){}
- static final Integer cache[] = new Integer[-(-128) + 127 + 1];
- static {
- for(int i = 0; i < cache.length; i++)
- cache = new Integer(i - 128);
- }
- }
根據上面的jdk源碼,java為了提高效率,IntegerCache類中有一個數組緩存了值從-128到127的Integer對象。當我們調用Integer.valueOf(int i)的時候,如果i的值是>=-128且<=127時,會直接從這個緩存中傳回一個對象,否則就new一個Integer對象。
具體如下:
Java代碼
- static final Integer cache[] = new Integer[-(-128) + 127 + 1]; //将cache[]變成靜态
- static {
- for(int i = 0; i < cache.length; i++)
- cache[i] = new Integer(i - 128); //初始化cache[i]
- }
這是用一個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代碼
- public class Test {
- public static void main(String args[]){
- Integer m = new Integer(5);
- Integer n = new Integer(5);
- System.out.println(m==n);
- m = m-1;
- n = n-1;
- System.out.println(m==n);
- }
- }
輸出什麼?
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代碼
- public class TestWhile{ //1
- public static void main(String[] args)//2
- {
- Integer i=0;//3
- Integer j=0;//4
- // Integer i=new Integer(0);//5
- // Integer j=new Integer(0);//6
- while(i<=j & i>=j & i!=j)//7
- { //8
- System.out.println("0000");//9
- }
- }
- }
那一行是拆箱?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代碼
- public boolean equals(Object obj) {
- if (obj instanceof Integer) {
- return value == ((Integer)obj).intValue();
- }
- return false;
- }
大家可以看到,隻要兩個Integer的int value是相等的,equals方法總是傳回true。