標籤:java 基本類型 裝箱基本類型 自動裝箱和拆箱
java中基本類型與裝箱基本類型“==”比較出現的幾種情況
java有一個類型系統有兩部分組成,包含基本類型(primitive),例如:int、double等,還有參考型別(reference type),例如:String、List。每個基本類型都有一個對應的參考型別,稱為裝箱基本類型(boxed promitive)。裝箱基本類型中對應於int、double的是Integer、Double。
Java 1.5發行版本中增加了自動裝箱和自動拆箱,自動裝箱和自動拆箱就是我們所知道的“文法糖”,這些特性模糊了但並沒有完全抹去基本類型和裝箱基本類型之間的區別。這兩種類型之間真正是有差別的,要很清楚的使用的是哪種類型,並且要對這兩種類型進行謹慎的選擇,這些都非常重要。
在基本類型和裝箱類型之間有三個主要區別。
- 第一:基本類型只有值,而裝箱基本類型則具有與他們的值不同的同一性。換句話說,兩個裝箱基本類型可以具有相同的值和不同的同一性。
- 第二:基本類型只有功能完備的值,而每個裝箱基本類型除了它對應基本類型的所有功能值之外,還有個非功能值:null。
- 第三:基本類型通常比裝箱基本類型更節省時間和空間。
如果你不小心上面的三點,這三點區別都會讓你陷入麻煩之中。
先看一個簡單的例子程式
如下
package com.wrh.firstpro;public class EqualsDemo06 { public static void main(String[] args) { System.out.println(myCompare(new Integer(4),new Integer(4))); } public static int myCompare(Integer i1,Integer i2){ return i1<i2?-1:((i1==i2)?0:1); }}
程式的輸出結果是:1
但是這兩個Integer執行個體都是表示相同的值4,為什麼函數myCompare返回的是1,而不是我們想象中的0呢?
原因如下:此分析參考《Effective Java中文版》第49條。
對錶達式 i1<i2執行計算會導致first和second引用的Integer執行個體被自動拆箱;也就是說,它提取了他們的基本類型值。計算動作要檢查產生的第一個int值是否小於第二個。答案是否定的。下一個測試就是執行計算運算式i1==i2,它在兩個對象上執行同一性的比較。如果i1和i2參考資料表示同一個int值的不同的Integer的執行個體,這個比較操作就會返回false,因此此函數錯誤的返回:1;
但是,我將這個檔案產生的.class檔案反編譯後,代碼如下:
package com.wrh.firstpro;import java.io.PrintStream;public class EqualsDemo06{ public static void main(String[] paramArrayOfString) { System.out.println(myCompare(new Integer(4), new Integer(4))); } public static int myCompare(Integer paramInteger1, Integer paramInteger2) { return paramInteger1 == paramInteger2 ? 0 : paramInteger1.intValue() < paramInteger2.intValue() ? -1 : 1; }}
發現,編譯器將return i1<i2?-1:((i1==i2)?0:1);變成了
return paramInteger1 == paramInteger2 ? 0 : paramInteger1.intValue() < paramInteger2.intValue() ? -1 : 1;
因此,我們根據反編譯後的程式碼分析運行結果:1的原因如下:先進行兩個Integer執行個體的同一性分析,不相等,然後通過Integer.intValue()將兩個Integer執行個體自動拆箱來比較大小,由於這兩個Integer執行個體表示相同的數值,因此,得到結果1
修正上面這個問題的方法如下:
添加兩個int型的中間變數,在這些中間變數上執行所有的比較操作可以避免同一性的比較。
public static int myCompare(Integer i1,Integer i2){ int temp1=i1; int temp2=i2; return temp1<temp2?-1:((temp1==temp2)?0:1); }
對封裝基本類型引用
==操作符幾乎總是錯誤的第一個例子程式:兩個都是通過new出來的Integer的執行個體進行“==”比較
package com.wrh.firstpro;public class EqualsDemo02 { public static void main(String[] args) { // TODO Auto-generated method stub Integer i7=new Integer(4); Integer i8=new Integer(4); System.out.println("i7==i8?"+(i7==i8)); System.out.println("i7.equals(i8)?"+i7.equals(i8)); }}
運行結果如下:
i7==i8?false
i7.equals(i8)?true
反編譯的代碼如下:
public class EqualsDemo02{ public static void main(String[] paramArrayOfString) { Integer localInteger1 = new Integer(4); Integer localInteger2 = new Integer(4); System.out.println(new StringBuilder().append("i7==i8?").append(localInteger1 == localInteger2).toString()); System.out.println(new StringBuilder().append("i7.equals(i8)?").append(localInteger1.equals(localInteger2)).toString()); }}
產生這個的原因在於應用“==”對兩個new出來的Integer類型的執行個體進行比較時,即時表示相同的數值,由於Integer類型的資料進行比較還要進行同一性的比較,因此不相等。而equals的比較結果是與我們的預期是一樣的。
總結:應用“==”對兩個new出來的Integer類型的執行個體進行比較時,即時表示相同的數值,由於Integer類型的資料進行比較還要進行同一性的比較,因此不相等
第二個例子程式:兩個通過自動裝箱得到的Integer執行個體進行“==”比較
在繼續往下面來看
package com.wrh.firstpro;public class EqualsDemo03 { public static void main(String[] args) { // TODO Auto-generated method stub Integer i3=3; Integer i4=3; System.out.println(i3==i4); System.out.println(i3.equals(i4)); }}
運行結果如下:
true
true
與上一個例子相比的區別在於將
Integer i7=new Integer(4);
Integer i8=new Integer(4);
換成了
Integer i3=3;
Integer i4=3;
就導致了該程式的運用==比較時,判斷是相等的,即Integer i3=3;Integer i4=3;使得i3、i4的同一性的相同的。
總結:通過“==”比較兩個自動裝箱的Integer執行個體時,只要兩者所表示的數值時相同的,結果就是相等的,因此他們的同一性是相同的。
上面例子的反編譯代碼如下
public class EqualsDemo03{ public static void main(String[] paramArrayOfString) { Integer localInteger1 = Integer.valueOf(3); Integer localInteger2 = Integer.valueOf(3); System.out.println(localInteger1 == localInteger2); System.out.println(localInteger1.equals(localInteger2)); }}
第三個例子程式:一個是通過new出來的Integer執行個體,一個是自動裝箱而得到的Integer執行個體進行“==”比較
程式如下:
package com.wrh.firstpro;public class EqualsDemo07 { public static void main(String[] args) { // TODO Auto-generated method stub Integer i1=1; Integer i2=new Integer(1); System.out.println(i1==i2); System.out.println(i2.equals(i1)); }}
程式運行結果如下:
false
true
結果比較好理解,這裡不再分析。
第四個例子程式:“==”左右兩邊都是Integer執行個體存在運算子運算的比較
繼續來看例子
package com.wrh.firstpro;public class EqualsDemo04 { public static void main(String[] args) { // TODO Auto-generated method stub Integer i1=1; Integer i2=2; Integer i3=3; System.out.println(i3==(i1+i2)); System.out.println(i3.equals(i1+i2)); }}
運行結果如下
true
true
即上面的比較都是先進行自動拆箱後然後進行數值上大小的比較。
通過反編譯後的代碼相信就可以很好的分析出答案了,代碼如下
public class EqualsDemo04{ public static void main(String[] paramArrayOfString) { Integer localInteger1 = Integer.valueOf(1); Integer localInteger2 = Integer.valueOf(2); Integer localInteger3 = Integer.valueOf(3); System.out.println(localInteger3.intValue() == localInteger1.intValue() + localInteger2.intValue()); System.out.println(localInteger3.equals(Integer.valueOf(localInteger1.intValue() + localInteger2.intValue()))); }}
總結:封裝類的“==”運算在遇到算術運算的情況下會自動拆箱,進行其代表的數值大小的比較
第五個例子程式
package com.wrh.firstpro;public class EqualsDemo05 { public static void main(String[] args) { // TODO Auto-generated method stub Integer i6=4; Long i5=4L; //System.out.println(i5==i6);//不能編譯,Integer類型和Long類型不能用“==”比較 System.out.println(i6.equals(i5)); }}
運行結果是:false,這個結果比較好理解
反編譯後的代碼如下:
public class EqualsDemo05{ public static void main(String[] paramArrayOfString) { Integer localInteger = Integer.valueOf(4); Long localLong = Long.valueOf(4L); System.out.println(localInteger.equals(localLong)); }}
總結
(1)反編譯的代碼中Integer.valueOf()和Integer.intValue()進行了自動裝箱和自動拆箱。
(2)應用“==”對兩個new出來的Integer類型的執行個體進行比較時,即時表示相同的數值,由於Integer類型的資料進行比較還要進行同一性的比較,因此不相等
(3)通過“==”比較兩個自動裝箱的Integer執行個體時,只要兩者所表示的數值時相同的,結果就是相等的,因此他們的同一性是相同的。
(4)通過equals來對來比較的時候,只要同類型(包括能自動裝箱和拆箱)代表的數值時相同的,就是相等的。
其它類型與Integer類型的自動裝箱和拆箱的原理相同,這裡不再贅述。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
java中基本類型與裝箱基本類型“==”比較出現的幾種情況