爲何會說是一致性?那就須要從代碼的原生講起了;咱們都知道"=="是java的元素運算符,而equals是Object類其中的一個方法。java
「==」運算符在java中的做用應分爲二種狀況:編程
一、應用於基本數據類型【byte、short、int、long、float、double、char、boolean】,用於比較存儲的值是否相等【值的內容】。數組
二、應用於引用數據類型【類(class)、接口(interface)、數組(array)】,用於比較所指向的對象地址是否相等。ide
「equals」是能用於比較引用類型,對於基本數據類型是沒有equals的。學習
從源碼入手:測試
* @param obj the reference object with which to compare. * @return {@code true} if this object is the same as the obj * argument; {@code false} otherwise. * @see #hashCode() * @see java.util.HashMap */ public boolean equals(Object obj) { return (this == obj); }
從以上源碼能夠看出equals底層也是先進行「==」比較,在進行別的操做。也印證了上面所說的「==」和equals是沒有區別這句話;惟一的區別:是基本類型沒有equals方法【沒有繼承Object類】,也就是說基本數據類型只能使用「==」進行比較兩值是否相同。ui
在使用Java編程過程當中,使用「equals」進行比較的操做,應用最多的應該是Striing類中的equals()方法。【String中的equals是重寫了Object中equals】this
老規矩直接從源碼入手:spa
1 * @param anObject 2 * The object to compare this {@code String} against 3 * 4 * @return {@code true} if the given object represents a {@code String} 5 * equivalent to this string, {@code false} otherwise 6 * 7 * @see #compareTo(String) 8 * @see #equalsIgnoreCase(String) 9 */ 10 public boolean equals(Object anObject) { 11 if (this == anObject) { 12 return true; 13 } 14 if (anObject instanceof String) { 15 String anotherString = (String)anObject; 16 int n = value.length; 17 if (n == anotherString.value.length) { //判斷兩個比較內容長度是否相等 18 char v1[] = value; 19 char v2[] = anotherString.value; 20 int i = 0; 21 while (n-- != 0) { //比較內容是否一致 22 if (v1[i] != v2[i]) 23 return false; 24 i++; 25 } 26 return true; 27 } 28 } 29 return false; 30 }
從源碼能夠看出,重寫的qulas仍是同樣先判斷「==」也就是兩個對象所指向的地址是否相同,若相同則直接返回true,若不相同在進行下一步比較,在返回true,若兩個都不知足則返回false。code
例如:
public static void main(String[] args) { String a = "Hello China"; String b = new String("Hello China"); String c = a; //值的傳遞,將a的內容傳遞給c //使用「==」和「equals」進行比較 //==對於引用數據類型比較的是指向的地址值是否相同 System.out.println(a == b);//false 地址不是指向同一個地方 System.out.println(a == c);//true 地址指向同一個地方 System.out.println(b == c);//false 地址不是指向同一個地方 //equals比較的是內容的值【先判斷==,==不知足在判斷值內容】 System.out.println(a.equals(b));//true 地址指向的內容是否一致 System.out.println(a.equals(c));//true 地址指向內容是都一致 System.out.println(b.equals(c));//true 地址指向內容是否一致 }
看了上面的例子,或許你仍是有點疑惑,爲了更加方便理解咱們直接上圖:
綠色的線是經過new出來的一個新的Striing對象,當new出一個先的對象時候,該對象會如今常量池中找是否存在該對象,若不存在則在常量池中建立一個字符串對象,而後將堆中該對象指向常量池中新建立的字符串對象。
附:需注意String中的intern()方法;【intern() 返回字符串對象的規範化表示形式。】
public static void main(String[] args) { String a = "Hello China"; String b = new String("Hello China"); b=b.intern(); System.out.println(a == b); //true System.out.println(a.equals(b)); //true }
intern()方法是檢查字符串池是否存在,若是存在則直接返回true。若沒有加 [b = b.intern()]則a==b應該返回false,加了intern()後當b在new一個新的對象前,會先到池裏面查找是否存在,存在則直接指向就不在建立。故a==b也就指向了同一個地址,a==b就成立返回true。
一、例如:咱們新建一個Student類,new兩個Student對象。這兩個對象的姓名、年齡、性別相同,咱們就認爲兩個學生對象相等,但對象地址不必定相同。【未重寫時兩個對象地址不一樣,重寫後則相同】
1 public class Test { 2 public static void main(String[] args) { 3 4 //未重寫時候的地址不一樣 5 Student student1 = new Student("張三",20,"男"); 6 System.out.println(student1);//@266474c2 7 Student student2 = new Student("張三",20,"男"); 8 System.out.println(student2);//@6f94fa3e 9 System.out.println(student1.equals(student2));//未重寫equals時爲 false 10 11 12 Student student1 = new Student("張三",20,"男"); 13 System.out.println(student1);//@2c63a8ab 14 Student student2 = new Student("張三",20,"男"); 15 System.out.println(student2);//@2c63a8ab 16 System.out.println(student1.equals(student2));//重寫equals後爲 true 17 18 } 19 } 20 21 22 class Student{ 23 public String name; 24 public int age; 25 public String sex; 26 27 28 29 30 @Override 31 public int hashCode() { 32 final int prime = 31; 33 int result = 1; 34 result = prime * result + age; 35 result = prime * result + ((name == null) ? 0 : name.hashCode()); 36 result = prime * result + ((sex == null) ? 0 : sex.hashCode()); 37 return result; 38 } 39 40 42 43 @Override 44 public boolean equals(Object obj) { 45 if (this == obj) 46 return true; 47 if (obj == null) 48 return false; 49 if (getClass() != obj.getClass()) 50 return false; 51 Student other = (Student) obj; 52 if (age != other.age) 53 return false; 54 if (name == null) { 55 if (other.name != null) 56 return false; 57 } else if (!name.equals(other.name)) 58 return false; 59 if (sex == null) { 60 if (other.sex != null) 61 return false; 62 } else if (!sex.equals(other.sex)) 63 return false; 64 return true; 65 } 66 67 68 69 70 public Student(String name, int age, String sex) { 71 this.name = name; 72 this.age = age; 73 this.sex = sex; 74 } 75 }
重寫equals大體是先判斷是都是同一個對象,而後在依次比較對象內的name,age,sex值。【由於String類內部已經重寫過equals因此直接使用equals就能夠比較內容】
平常開發中,通常是要重寫equals才符合實際生活狀況!
可是隨着重寫equals又出現了新的問題,爲何還要重寫hashCode,不重寫會怎麼樣!!!
hashcode方法是根據對象的地址轉換以後返回的一個哈希值,使用hashcode方法,會返回一個哈希值,哈希值對數組的長度取餘後會肯定一個存儲的下標位置。不一樣的哈希值取餘以後的結果多是相同的,相同的時候就用equals方法判斷是否爲相同的對象,不一樣則在鏈表中插入。若哈希值取餘後不相同,則插入的位置也不一樣,兩個對象確定不相同。【結合圖解】
小結:
通過上面描述後,在判斷的時先根據hashcode進行的判斷,相同的狀況下再根據equals()方法進行判斷。若是隻重寫了equals方法,而不重寫hashcode的方法,會形成hashcode的值不一樣,而equals()方法判斷出來的結果爲true。【地址不一樣狀況下也返回true】。而咱們須要的是兩個對象在相同狀況下在進行equals判斷,所以重寫hashCode是避免地址值不相同狀況下也進行覆蓋。
爲了更進一步理解,這邊使用HashMap的鍵值對來進行測試【HashMap的鍵不能重複,底層基於數組+鏈表+紅黑樹結構】==》鏈表長度大於8時候轉換爲紅黑樹
一、不重寫hashCode()方法:
1 public class Test { 2 public static void main(String[] args) { 3 Student student1 = new Student("張三",20,"男"); 4 System.out.println("student1的hashCode值:"+student1.hashCode());// 5 Student student2 = new Student("張三",20,"男"); 6 System.out.println("student1的hashCode值:"+student2.hashCode());// 7 System.out.println("比較兩個對象是否相等:"+student1.equals(student2));//未重寫equals時爲 false 8 HashMap<Student,Integer> studentMap = new HashMap(); 9 studentMap.put(student1,111); 10 studentMap.put(student2,222); 11 System.out.println("當前的map大小:"+studentMap.size());// 12 13 } 14 }
輸出結果:
二、重寫hashCode()方法:
輸出結果:
很明顯,在沒有重寫hashCoede()的時候,他們的hash值不一樣,也就是存儲在數組中的位置不一樣,但由於重寫了equals(),因此內容是一致的。但在不能出現重複鍵的Map中,他們仍是沒有進行覆蓋,顯然這不符合實際邏輯。
在重寫hashCode()以後,在進行map存儲時候會先判斷hashCode值是否相同,相同則指向存儲數組中的同一個位置,在進行內容判斷,內容相同才進行覆蓋操做,故返回的size爲1,若不相同則使用裏鏈表方式,存放於後面。
小結:
一、不重寫hashCode跟equals時候返回確定爲false,由於原生Object的equals判斷的是地址值【==】。
二、不重寫hashCode,重寫equals返回的是true,在進行存儲時指向的位置下標不一樣,但因爲重寫判斷內容,因此使用equals判斷內容時候會返回true。
三、重寫hashCoe,不重寫equals返回false,由於兩個對象的地址相同了,但未重寫比較內容,所以返回false【鏈表狀況】。
四、重寫了hashCode,重寫了equals返回true,這個時候在map中指向的數組下標一致,內容也一致,就會產生覆蓋。
【簡單來講:重寫hashCode()是爲了讓二者在內存中指向同一個地方,而重寫equals()是爲了在指向同一個地方同時判斷是都徹底一致】
如有不但之處,歡迎指正!一塊兒學習!!!!