== equals 自動裝箱 自動拆箱
問題描述:
定義了兩個Integer變數,為Integer a= 200; Integer b= 200; , 但我比較的時候 if (a == b),卻返回false,不都是200嗎?為什麼返回false,後來改為equals(),返回true,這其中的端倪,且聽我下面分析:
在Object類中,equals方法的定義是這樣的,
-
public boolean equals(Object obj)
-
{
-
return (
this == obj);
-
}
看到這塊,懵了,這不還是用==來進行比較的嗎?
原因分析:在大部分的封裝類中,都重寫了Object類的這個方法,所以兩者還是會有區別的。總的來說,==是一個關係運算符,如果比較的兩端都為基本型別,則判斷兩者的值是否相等,(判斷過程中還有不同基本型別的轉化,這裡不做討論),如果比較的兩端都為引用型別的話,則比較兩者所指向物件的地址是否相同;對於equals方法,首先,能呼叫這個方法肯定是一個物件,然後,如果這個物件所在的類重寫了equals方法,則按照重寫的方法進行比較,如果沒有,則比較兩者所指向物件的地址是否相同。
接下來就自己動手寫測試程式驗證,程式碼如下:
-
1.public class Test{
-
2.
public static void main(String[] args) {
-
3.
Integer a =
new Integer(
200);
-
4. Integer b =
new Integer(
200);
-
5. Integer c =
200;
-
6. Integer e =
200;
-
7.
int d =
200;
-
8.
-
9. System.
out.println(
"兩個new出來的物件 ==判斷"+(a==b));
-
10. System.
out.println(
"兩個new出來的物件 equal判斷"+a.
equals(b));
-
11. System.
out.println(
"new出的物件和用int賦值的Integer ==判斷"+(a==c));
-
12. System.
out.println(
"new出的物件和用int賦值的Integer equal判斷"+(a.
equals(c)));
-
13. System.
out.println(
"兩個用int賦值的Integer ==判斷"+(c==e));
-
14. System.
out.println(
"兩個用int賦值的Integer equal判斷"+(c.
equals(e)));
-
15. System.
out.println(
"基本型別和new出的物件 ==判斷"+(d==a));
-
16. System.
out.println(
"基本型別和new出的物件 equal判斷"+(a.
equals(d)));
-
17. System.
out.println(
"基本型別和自動裝箱的物件 ==判斷"+(d==c));
-
18. System.
out.println(
"基本型別和自動裝箱的物件 equal判斷"+(c.
equals(d)));
-
19. }
-
20.}
執行的結果如下:
-
兩個new出來的物件 ==判斷false
-
兩個
new出來的物件 equal判斷
true
-
new出的物件和用
int賦值的Integer ==判斷
false
-
new出的物件和用
int賦值的Integer equal判斷
true
-
兩個用
int賦值的Integer ==判斷
false
-
兩個用
int賦值的Integer equal判斷
true
-
基本型別和
new出的物件 ==判斷
true
-
基本型別和
new出的物件 equal判斷
true
-
基本型別和自動裝箱的物件 ==判斷
true
-
基本型別和自動裝箱的物件 equal判斷
true
首先,第9行中,對於兩個new出來的Integer物件,用==比較兩者,得到了false,原因分析如下:
每次使用new關鍵字,都會在堆記憶體中申請一塊空間,存放相應的物件的值,然後在棧中存放這塊記憶體的引用地址。而==運算子比較兩者所指向物件的地址是否相同,申請了兩塊空間,地址肯定不相同,所以結果為false。
第10行中,結果為true,我們先檢視一下java的原始碼,發現Integer重寫了equals方法如下:
-
public boolean equals(Object obj) {
-
if (obj
instanceof Integer) {
-
return value == ((Integer)obj).intValue();
-
}
-
return
false;
-
}
由上面的原始碼可知,equals方法又呼叫了一個intValue方法,繼續檢視intValue方法原始碼:
-
public int intValue()
-
{
-
return
value;
-
}
看完原始碼,我們分析一下:
首先,判斷傳進來的Object型別是不是Integer類的一個例項,如果不是直接返回false;
如果是則判斷兩者的成員變數value值是不是相等的(Integer類中定義的private final int value),這塊又回到了基本型別的比較。value的值在建立這個物件的時候被賦值,兩個Integer物件傳遞的引數都為200,所以value值相等,返回true。
看第11行前,先看下第5行。第5行中,用int給Integer賦值的那條語句,從jdk1.5開始能這麼做了,因為從這個版本開始,java引入了自動裝箱、自動拆箱機制。第5行就是一個自動裝箱的過程,相當於:Integer c = Integer.valueOf(200);
在Integer類中,valueOf方法是這麼實現的:
-
public static Integer valueOf(int i) {
-
assert IntegerCache.high >=
127;
-
if (i >= IntegerCache.low && i <= IntegerCache.high)
-
return IntegerCache.cache[i + (-IntegerCache.low)];
-
return
new Integer(i);
-
}
上面這段程式碼首先規定了一個範圍,預設IntegerCache.low 是-127,Integer.high是128,如果引數中的i在這個範圍之內,則返回一個數組中的內容,如果不在這個範圍,則new一個新的Integer物件並返回。檢視Integer類的原始碼可以發現,這個數組裡面快取了基本型別-128-127之間的Integer物件。但是由於第11行是與一個new出來的物件做比較,所以==肯定返回的false。
第12行,equals方法比較兩個物件的value值,所以為true。
第13行,兩個自動裝箱的變數,但是裝箱傳遞的值大於127,所以返回false。這這塊又試了下在-128到127之間的數,結果為true,大家可以試下。
第14行,結果為true。兩個自動裝箱的Integer物件,比較value。
第15行,這塊進行比較的時候,會對Integer物件進行自動拆箱,也就是呼叫intValue方法,方法如上。兩個基本資料型別進行==判斷,根據值比較,所以結果為true。這塊可能有人會問,為什麼不是對int型別進行自動裝箱處理呢?其實這塊是java根據一個很明顯的道理進行設計的:如果有人比較一個int型別的值和Integer型別的值,是想比較什麼呢?肯定是值呀,所以這塊是對Integer物件進行拆箱而不是對int型別裝箱了。
第16行這塊,首先呼叫equals方法的肯定是Integer物件,但是Integer類中重寫的equals方法引數是一個Object型別呀,怎麼能傳遞一個基本資料型別進去呢?所以,這塊又是一個自動裝箱的表現,當傳遞一個int型別給equals這個方法時,java會自動將這個值打包裝箱為Integer類,而Integer類的最終父類又是Object,所以這塊引數的問題就解決了,然後就是兩個Integer物件進行equals判斷,返回true。
第17行,首先d為一個基本型別int,c為一個Integer物件,所以進行==比較的時候,肯定會對Integer物件進行拆箱處理,所以結果為true。
第18行,同第16行。
總結
“==比較地址,equals比較值”這個還是挺靠譜的。但是對於包裝類和基本型別,還要涉及它們的自動裝箱、自動拆箱,所以小心一點還是比較好的,尤其是在以後的面試中,不要走到別人挖的陷阱中。
1. Integer 型別的值在[-128,127] 期間,Integer 用 “==”是可以的 , Integer 與 int 型別比較(==)比較的是值。