一、前言
說到==和equals的問題,面試的時候可能經常被問題到,有時候如果你真的沒有搞清楚里邊的原因,被面試官一頓繞就懵了,所以今天我們也來徹底了解一下這個知識點。
二、==和equals的作用
2.1 ==的作用
在java中我們用==來判斷兩個變量是否相等,但是會根據數據類型有所區別:
1.對於8種基礎數據類型(byte、short、int、long、double、float、boolean、char)來說==是判斷變量的數值是否相等。
byte y1 = 1, y2 = 1; short s1 = 1, s2 = 1; int i1 = 1, i2 = 1; long l1 = 1, l2 = 1; double d1 = 1, d2 = 1; float f1 = 1, f2 = 1; boolean b1 = true, b2 = true; char c1 = 1, c2 = 1;
System.out.println("byte:y1==y2 " + (y1 == y2)); System.out.println("short:s1==s2 " + (s1 == s2)); System.out.println("int:i1==i2 " + (i1 == i2)); System.out.println("long:l1==l2 " + (l1 == l2)); System.out.println("double:d1==d2 " + (d1 == d2)); System.out.println("float:f1==f2 " + (f1 == f2)); System.out.println("boolean:b1==b2 " + (b1 == b2)); System.out.println("char:c1==c2 " + (c1 == c2));
對於引用類型,==比較的是引用的地址是否相等:
Object o1 = new Object(); Object o2 = o1; Object o3 = new Object(); System.out.println("Object:o1 == o2 " + (o1 == o2)); System.out.println("Object:o1.equals(o2) " + (o1.equals(o2))); System.out.println("Object:o1 == o3 " + (o1 == o3)); System.out.println("Object:o1.equals(o3) " + (o1.equals(o3)));
接下來我們來看看equals里的內部實現,其實還是調用的==:
總結:所以說對於==,當數據類型是8大基礎類型時,比較的是(棧中)數值是否相等,當數據類型是引用類型時,比較的是對象的引用地址是否相等。這是通過jvm來自動判斷的。
2.2 equals的作用
equals是object類中的一個方法,也就是說使用equals必須是對象類型,所以equals比較的是對象是否相當,默認情況上邊已經說過了使用的就是==,但是別忘了equals是基類中的方法,是可以重寫的,當我們重寫的時候就可以實現不同的功能了。
那么對於字符串,也是一種引用類型,我們先來看看下面這個例子:
String str1 = "123",str2 = "123"; String str3 = new String("123"); String str4 = new String("123");
System.out.println("String:str1 == str2 " + (str1 == str2)); System.out.println("Object:str1.equals(str2) " + (str1.equals(str2))); System.out.println("Object:str1 == str3 " + (str1 == str3)); System.out.println("Object:str1.equals(str3) " + (str1.equals(str3))); System.out.println("Object:str3 == str4 " + (str3 == str4)); System.out.println("Object:str3.equals(str4) " + (str3.equals(str4)));
這里我們介紹一個概念,叫做字符串拘留池:由於字符串這種東西在我們的程序中非常的常用,每一個字符串都要創建對象,花費開銷,但是通常字符串一般的我們只關心它的值是什么,所以jvm在這里做了一個優化,當字符串在拘留池(一塊存儲字符串的地方)里出現的時候,如果下次我們又用到了這個字符串,就直接返回池子里字符串的地址,因為值都是一樣的,減少了開銷,如果沒有,就放進池子里,以備后來有用到的時候,相當於一個緩存了。
所以當我們執行String str1 = "123",str2 = "123"; 實際上兩個變量指向的是同一個地址,所以自然的str1 == str2和str1.equals(str2)是一樣的,都為true,沒毛病。
當我們使用String str3 = new String("123");很明顯示開辟了一塊新的內存地址,只不過存儲的值是123是一樣的,所以str1 == str3是false,沒毛病,但是按理說str1.equals(str3)也應該是false啊,之前不是說內部也是用的==嗎,我們可以看一下字符串類中equals的實現:
很明顯,在實現判斷了地址是否相同以后,這里邊又做了判斷,字符串的值是否相等,如果相等就返回true,到這里為止,我們已經真相大白了,原來String類重寫equals方法,判斷的是字符串的值是否相等,而不僅僅是地址。所以自然的我們的str1.equals(str3) 是true了。
那么由此可知,str3 == str4 比較的是地址,明顯不一樣,因為是new了兩個對象,所以返回false,str3.equals(str4)比較的是字符串的值,所以返回true,也沒毛病。
總結:因為我們在對字符串比較的時候,往往關注的就是值一樣不一樣,所以java這樣重寫也是 很有必要的,所以我們一直推薦判斷字符串相等用equals方法,而不是用==來判斷。equals方法具體產生什么樣的效果,完全看子類是怎么重寫的,比如Date類,重寫了equals方法,只要兩個時間的getTime()時間戳一樣,那么就相等,這也是符合我們的認知的。
所以,在我們以后需要判斷兩個對象相等的用equals的時候,我們可以完全根據自己的業務來重寫equals方法,比如兩個Person實例,如果id一樣,我們就應該認為他們倆是相等,這個時候我們就可以重寫equals方法,用id作比較來返回值。
三、總結
這次終於搞明白了==和equals的用法和作用,以后學什么東西還必須要深入理解一下內部原理才行,這樣就不怕面試官再來搞事情了。