引言:在理解equals和==的區(qū)別之前,必須先搞懂 棧和堆之間的區(qū)別。
總的來說:
值類型是存儲在內(nèi)存中的棧中,而引用類型的變量在棧中存儲的僅僅是引用類型變量的地址,而引用類型本身是存儲在堆中。
equals 和 == 的區(qū)別:
- == 操作比較的是兩個變量的值是否相等,對于引用型變量表示的是兩個變量在堆中存儲的地址是否相同,即棧中的內(nèi)容是否相同。
- equals操作表示的是兩個變量是否是對同一個對象的引用,即堆中的內(nèi)容是否相同。
- 更簡單的說法:==比較的是地址,equals比較的是內(nèi)容
例子:字符串的比較
public class EqualsTest {
public static void main(String[] args){
String s1 = "Test";
String s2 = "Test";
System.out.println("s1 == s2 : " + (s1 == s2));
System.out.println("s1.equals(s2): " + s1.equals(s2));
}
}
輸出結(jié)果:
s1 == s2 : true
s1.equals(s2): true
接下來,做些改動:將s2通過new的方式來創(chuàng)建
public class EqualsTest {
public static void main(String[] args){
String s1 = "Test";
String s2 = new String("Test");
System.out.println("s1 == s2 : " + (s1 == s2));
System.out.println("s1.equals(s2): " + s1.equals(s2));
}
}
輸出結(jié)果:
s1 == s2 : false
s1.equals(s2): true
我們發(fā)現(xiàn)這時候==操作結(jié)果為false了,equals方法依舊不變。
實際上,這是字符串緩沖池所起的作用。當我們在創(chuàng)建s1字符串后,s1便存放在字符串緩存池中。
- 對于第一種情況,創(chuàng)建s2時,程序會在緩沖池中尋找到具有相同值的s1,因此s2和s1共同引用對象"Test"。也就是說s1和s2的引用地址相同,那么自然引用對象也是相同的咯。所以兩者都為true。
- 對于第二種情況,我們通過new方式創(chuàng)建了s2,相當于告訴程序“我要個新的,不要舊的”。因此,程序會重新創(chuàng)建一個"Test"對象在內(nèi)存中,雖然它們的值相同,但它們的位置是不同的。因此,它們的引用地址自然是不同的,==操作自然為false,equals為true,因為它們的內(nèi)容還是相同的。
當然,如果我們希望s2是從緩沖池中拿到對象,可以調(diào)用intern()方法實現(xiàn):
public class EqualsTest {
public static void main(String[] args){
String s1 = "Test";
String s2 = new String("Test").intern();
System.out.println("s1 == s2 : " + (s1 == s2));
System.out.println("s1.equals(s2): " + s1.equals(s2));
}
}
輸出結(jié)果:
s1 == s2 : true
s1.equals(s2): true
也就是說,通過intern()方法防止了程序創(chuàng)建重復的對象。
總結(jié):
- == 比較的是兩個對象的地址,而equals比較的是兩個對象的內(nèi)容。
- 地址相同的兩個對象自然內(nèi)容也是相同的,即滿足==為true,則equals必然為true。但注意對象內(nèi)容相同的,地址不一定相同,因此,滿足equals為true,但==不一定為true。