我們先看代碼
String str1 =
new
String(
"hello"
);
String str2 =
"hello"
;
System.out.println(
"str1==str2: "
+ (str1==str2)); \\
1
System.out.println(
"str1.equals(str2): "
+ str1.equals(str2)); \\
2
|
輸出結果:
str1==str2:
false
str1.equals(str2):
true
|
關於==和equals,我們需要知道java中的數據類型,可分為兩類:
1.基本數據類型,也稱原始數據類型。byte,short,char,int,long,float,double,boolean
他們之間的比較,應用雙等號(==),比較的是他們的值。
2.復合數據類型(類)
當他們用(==)進行比較的時候,比較的是他們在內存中的存放地址,所以,除非是同一個new出來的對象,他們的比較后的結果為true,否則比較后結果為false。 JAVA當中所有的類都是繼承於Object這個基類的,在Object中的基類中定義了一個equals的方法,這個方法的初始行為是比較對象的內存地 址,但在一些類庫當中這個方法被覆蓋掉了,如String,Integer,Date在這些類當中equals有其自身的實現,而不再是比較類在堆內存中的存放地址了。
對於復合數據類型之間進行equals比較,在沒有覆寫equals方法的情況下,他們之間的比較還是基於他們在內存中的存放位置的地址值的,因為Object的equals方法也是用雙等號(==)進行比較的,所以比較后的結果跟雙等號(==)的結果相同。
因為string屬於符合數據類型,所以應該是使用equals,假如我們使用==比較,肯定是比較它們的內存地址了,所以\\1 \\2 的結果顯而易見了
要想判斷兩個對象是否相等,不能通過比較兩個對象的引用是否相等,這是永遠都得不到相等的結果的,因為兩個對象的引用永遠不會相等,所以正確的比較方法是直接比較這兩個對象,比較這兩個對象的實質是不是一樣的,即這兩個對象里面的內容是不是相同的,通過比較這兩個對象的屬性值是否相同而決定這兩個對象是否相等。
Object類提供了一個equals()方法來比較兩個對象的內容是否相同,因此我們可以采用這個方法去比較兩個對象是否在邏輯上“相等”。如:c1.equals(c2);這里是調用從Object類繼承下來的equals()方法,通過查閱API文檔得到Object類里的equals方法的定義如下:
public boolean equals(Object obj)
注意,在重寫equals方法時,需要按照以下幾個規則設計:
1自反性 :對任意引用值X,x.equals(x)的返回值一定為true.
2對稱性: 對於任何引用值x,y,當且僅當y.equals(x)返回值為true時,x.equals(y)的返回值一定為true;
3傳遞性:如果x.equals(y)=true, y.equals(z)=true,則x.equals(z)=true
4一致性:如果參與比較的對象沒任何改變,則對象比較的結果也不應該有任何改變
5非空性:任何非空的引用值X,x.equals(null)的返回值一定為false
再看代碼:
String str2 =
"hello"
;
String str3 =
"hello"
;
System.out.println(
"str3==str2: "
+ (str3==str2)); \\
3
System.out.println(
"str3.equals(str2): "
+ str3.equals(str2)); \\
4
|
輸出結果:
str3==str2:
true
str3.equals(str2):
true
|
這里的又為什么都是輸出true呢,因為它們都是從緩沖池取出來的,由於string類比較特殊,jdk專門做了緩存優化。
原來Java運行時會維護一個String Pool(String池)。String池用來存放運行時中產生的各種字符串,並且池中的字符串的內容不重復。而一般對象不存在這個緩沖池,並且創建的對象僅僅存在於方法的堆棧區。
也就是說需要看string創建的方式:
1 當使用任何方式來創建一個字符串對象s時,Java運行時(運行中JVM)會拿着這個X在String池中找是否存在內容相同的字符串對象,如果不存在,則在池中創建一個字符串s,否則,不在池中添加。
2 Java中,只要使用new關鍵字來創建對象,則一定會(在堆區或棧區)創建一個新的對象。
3 使用直接指定或者使用純字符串串聯來創建String對象,則僅僅會檢查維護String池中的字符串,池中沒有就在池中創建一個,有則罷了!但絕不會在堆棧區再去創建該String對象。
4 使用包含變量的表達式來創建String對象,則不僅會檢查維護String池,而且還會在堆棧區創建一個String對象。
另外,String的intern()方法是一個本地方法,定義為public native String intern(); intern()方法的價值在於讓開發者能將注意力集中到String池上。當調用 intern 方法時,如果池已經包含一個等於此 String 對象的字符串(該對象由 equals(Object) 方法確定),則返回池中的字符串。否則,將此 String 對象添加到池中,並且返回此 String 對象的引用。