(转)Java中equals和==、hashcode的区别
?背景:學習輝哥總結的基礎知識,從頭來,直面短板。
1 問題引入及分析
請看下面的代碼清單1
@Testpublic void test01() {String a = "a" + "b" + 1; String b = "ab1"; System.out.println(a == b);}上述這段代碼來源自謝宇編著的書籍《Java特種兵》上冊。
代碼清單1中的輸出是
true?
這是個考察Java基本功的問題,類似的問題還有很多,如清單2:
public static String getA() { return "a"; } @Testpublic void test2(){ String a = "a"; final String c = "a"; String b = a + "b"; String d = c + "b"; String e = getA() + "b"; String compare = "ab"; System.out.println(b == compare); System.out.println(d == compare); System.out.println(e == compare); }?結果:
false true false?
要理解這個問題,首先需要搞清楚這些:
1.1 關于“==”
“==”是用于匹配內存單元上的內容,在Java語言中,當使用“==”匹配時,其實就是對比兩個內存單元的內容是否一樣。
如果是原始類型byte、boolean、short、char、int、long、float、double,就是直接比較它們的值。
如果是引用(Reference),比較的就是引用的值,“引用的值”可以被認為是對象的邏輯地址。
如果兩個引用發生“==”操作,就是比較相應的兩個對象的地址值是否一樣。
換一句話說,如果兩個引用所保存的對象是同一個對象,則返回true,否則返回false。如果a、b兩個引用都指向null,則也是返回true。
1.2 編譯時的優化
在代碼清單1中,a引用是通過“+”賦值的,b引用是通過直接賦值的,那么為什么a和b兩個引用會指向同一個內存單元?這就是JVM的“編譯時優化”。
當編譯器在編譯代碼 String a = "a" + "b" + 1 時,會將其編譯成 String a = "ab1" ,因為都是“常量”,編譯器認為這3個常量疊加會得到固定的值,無需運行時再進行計算,所以就會這樣優化。
類似的還有 int i = 2 * 3 + 1,并不是在實際運行時再計算i的值,而是在編譯時直接變成了i=7。
?
而在代碼清單2中,b與compare比較時,由于compare是個常量,而b不是常量,原因是b = a + "b",a并不是一個常量,a中的值可以修改,因此編譯器不會進行優化。
變量c有一個final修飾符,從定義上約束了c是不允許改變的,因此編譯器會進行優化。
變量e的值來自一個方法,雖然方法內返回一個常量的引用,但是編譯器并不會去看方法內部做了什么,所以編譯器不會進行優化。
常量時會進行“編譯時優化”
1.3 關于“equals()”
首先來看看Object中的equals()方法的實現:
public boolean equals(Object obj) { //調用equal的對象的地址和參數對象的地址是否相等 return (this == obj); }?從源碼中可以看出,Object類中equals()方法是使用“==”來匹配的,也就是說,如果不去重寫equals()方法,那么默認的equals()操作就是對比對象的地址。
equals()方法的存在就是希望子類去重寫這個方法的 ,而在String類中,就重寫了equals()方法:
public boolean equals(Object anObject) { // 如果是同一個對象 if (this == anObject) { return true; } // 如果傳遞進來的參數是String類的實例 if (anObject instanceof String) { String anotherString = (String) anObject; int n = count;// 字符串長度 if (n == anotherString.count) // 如果長度相等就進行比較 { char v1[] = value;// 取每一個位置的字符 char v2[] = anotherString.value; int i = offset; int j = anotherString.offset; while (n-- != 0) // 對于每一位置逐一比較 { if (v1[i++] != v2[j++]) return false; } return true; } } return false; }在JDK 1.6中,String.equals()的代碼邏輯大致是這樣的:
?
1. 判定傳入的對象和當前對象是否為同一個對象,如果是就直接返回true;
2. 判定傳入的對象類型是否為String,若不是則返回false;
3. 判定傳入的String與當前String的長度是否一致,如不一致就返回false;
4. 循環對比兩個字符串的char[]數組,逐個比較字符是否一致,如果不一致就直接返回false;
5. 循環結束都沒有找到不匹配的,最后返回true。
?覆寫equals時的相關準則
自反性:對于任何非空引用值 x,x.equals(x) 都應返回 true。
對稱性:對于任何非空引用值 x 和 y,當且僅當 y.equals(x) 返回 true 時,x.equals(y) 才應返回 true。
傳遞性:對于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true, 并且 y.equals(z) 返回 true,那么 x.equals(z) 應返回 true。
一致性:對于任何非空引用值 x 和 y,多次調用 x.equals(y) 始終返回 true 或始終返回 false, 前提是對象上 equals 比較中所用的信息沒有被修改。
非空性:對于任何非空引用值 x,x.equals(null) 都應返回 false
?
參考:面試官愛問的equals與hashCode
?
?
?2 先前的總結
java中的數據類型,可分為兩類:? 1.基本數據類型,也稱原始數據類型。byte,short,char,int,long,float,double,boolean?他們之間的比較,應用雙等號(==),比較的是他們的值。? 2.復合數據類型(類)?當他們用(==)進行比較的時候,比較的是他們在內存中的存放地址,所以,除非是同一個new出來的對象,他們的比較后的結果為true,否則比較后結果為false。?JAVA當中所有的類都是繼承于Object這個基類的,在Object中的基類中定義了一個equals的方法,這個方法的初始行為是比較對象的內存地 址,但在一些類庫當中這個方法被覆蓋掉了,如String,Integer,Date在這些類當中equals有其自身的實現,而不再是比較類在堆內存中的存放地址了。
(注意這里說的,String類本身在實現equals()方法時候就覆蓋了自身的equals,文章的末尾有其源碼的實現)對于復合數據類型之間進行equals比較,在沒有覆寫equals方法的情況下,他們之間的比較還是基于他們在內存中的存放位置的地址值的,因為Object的equals方法也是用雙等號(==)進行比較的,所以比較后的結果跟雙等號(==)的結果相同。(對于普通的類還是按照這個標準的) public class TestString {public static void main(String[] args) {String s1 = "Monday";String s2 = "Monday";if (s1 == s2) { System.out.println("s1 == s2");}else {System.out.println("s1 != s2");}} } 編譯并運行程序,輸出: s1 == s2
?
說明:s1 與 s2 引用同一個 String 對象 -- "Monday"! 2.再稍微改動一下程序,會有更奇怪的發現: @Testpublic void test04(){String s1 = "Monday";String s2 = new String("Monday");if (s1 == s2) {System.out.println("s1 == s2");}else {System.out.println("s1 != s2");}if (s1.equals(s2)) {System.out.println("s1 equals s2");} else {System.out.println("s1 not equals s2");}} 輸出: s1 != s2 s1 equals s2 說明s1 和s2 分別引用的是兩個不同的對象。但是由于String 類覆寫了equals(),所以實際比較的是字符串的內容。3. 字符串緩沖池(這個概念可以參考認識java中的堆和棧)
原來,程序在運行的時候會創建一個字符串緩沖池。當使用 s2 = "Monday" 這樣的表達是創建字符串的時候,程序首先會在這個String緩沖池中尋找相同值的對象,在第一個程序中,s1先被放到了池中,所以在s2被創建的時候,程序找到了具有相同值的 s1,將s2引用s1所引用的對象"Monday"
第二段程序中,使用了 new 操作符,他明白的告訴程序:"我要一個新的!不要舊的!"于是一個新的"Monday"Sting對象被創建在內存中。他們的值相同,但是位置不同,一個在池中游泳一個在岸邊休息。哎呀,真是資源浪費,明明是一樣的非要分開做什么呢?(這就是為啥==不一致的原因,如果不是string類型重寫equals的話,這個兩個對象的equals結果也會不等)
4.再次更改程序:
@Testpublic void test05(){String s1 = "Monday";String s2 = new String("Monday");s2 = s2.intern(); //intern()方法操作過程if (s1 == s2) {System.out.println("s1 == s2");}else {System.out.println("s1 != s2");}if (s1.equals(s2)) {System.out.println("s1 equals s2");} else {System.out.println("s1 not equals s2");}} s1 == s2 s1 equals s2?
原 來,(java.lang.String的intern()方法"abc".intern()方法的返回值還是字符串"abc",表面上看起來好像這個方 法沒什么用處。但實際上,它做了個小動作:檢查字符串池里是否存在"abc"這么一個字符串,如果存在,就返回池里的字符串;如果不存在,該方法會 把"abc"添加到字符串池中,然后再返回它的引用。)
hashcode和equals之間的關系
(轉)從一道面試題徹底搞懂hashCode與equals的作用與區別及應當注意的細節?轉載于:https://www.cnblogs.com/lixuwu/p/5676207.html
總結
以上是生活随笔為你收集整理的(转)Java中equals和==、hashcode的区别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: git 修改上次提交信息 与 撤销此操
- 下一篇: Android MediaPlayer