Java中==與equals()及hashcode()三者之間的關(guān)系詳解
1.= =
=為賦值運(yùn)算符,==為比較運(yùn)算符,僅比較對(duì)象的內(nèi)存地址,無法比較真正意義上的相等!
JDK里的equals方法就是通過==來實(shí)現(xiàn)的比較對(duì)象的內(nèi)存地址
以Integer為例
Integer a = 127; Integer b = 127; System.out.println(a == b);//true Integer c = 128; Integer d = 128; System.out.println(c == d);//false
這里也是通過== 引出一個(gè)知識(shí)點(diǎn),一個(gè)數(shù)值之差為啥導(dǎo)致結(jié)果不一樣?在[-128,127]的區(qū)間內(nèi)Integer a = 127;由于設(shè)計(jì)了緩存,而后的Integer b = 127;就是直接利用的緩存里的數(shù)值對(duì)象,所以通過==比較的結(jié)果為true,因?yàn)樗麄儽举|(zhì)還是一個(gè)數(shù)值對(duì)象
而Integer c = 128;Integer d = 128;就沒這樣幸運(yùn)了,超過了緩存區(qū)間會(huì)重新new出對(duì)象,以至于兩者雖然數(shù)值相同但是地址不同
所以啊,==比較的是地址!也只是地址!
在前面==的基礎(chǔ)上,再來看equals()
2.equals()
以一個(gè)String類型的變量為例,當(dāng)我們來使用equals()比較兩個(gè)對(duì)象時(shí),結(jié)果肯定是false,因?yàn)閑quals()方法比較的是內(nèi)存地址,這里的person1,person2均是兩次new出來的,所以地址肯定是不相同的,而person1,person3指向同一空間地址一定是相同的
Person person1 = new Person("lyy"); Person person2 = new Person("lyy"); Person person3 = person1; System.out.println(person1.equals(person2));//false System.out.println(person1.equals(person3));//true
在不重寫的情況下,我們Ctrl+B看一下equals()的源碼:
public boolean equals(Object obj) { return (this == obj); }
顯而易見的是 (this = = obj)是該方法的核心,而 = = 又是兩個(gè)對(duì)象比較的方式,= = 嘛比的是內(nèi)存地址,懂的都懂噢
3.重寫equals()
記不記得你在刷面經(jīng)或者短視頻的時(shí)候經(jīng)常看到的一句話——比較兩個(gè)對(duì)象的內(nèi)容是否相等時(shí)我們要重寫equals()方法!
這是為何?那我們不妨來重寫一下equals()試試水
@Override public boolean equals(Object obj){ if (this==obj){ return true; } if (obj==null||getClass()!= obj.getClass()){ return false; } Person person=(Person)obj; return Objects.equals(name,person.name); }
重寫過equals后,原有的兩者就已發(fā)生翻天覆地的變化,從原來的比較內(nèi)存地址——>比較對(duì)象內(nèi)容,這是一件很神奇的事情,因?yàn)閷?shí)現(xiàn)了比較不同對(duì)象的相同或者不同內(nèi)容!
重寫之后:
具體是如何實(shí)現(xiàn)的呢?就像下面這樣…
4.equals()比較流程
下面我們來探索一下重寫的equals()是如何比較內(nèi)容的:
通過debug來深入理解一下
下面來看一下debug過程中變量情況
總的來說,通過debug,重寫equals()來比較不同變量的不同或者相同內(nèi)容得到了進(jìn)一步論證!
5.hashcode()
我們?cè)贗DEA中通過CTRL+O的快捷鍵重寫hashcode()時(shí)它上面所屬的類是誰?
java.lang.Object!
顯而易見,該方法是Object類所定義的方法,作用是返回對(duì)象的哈希值返回值的類型為int(哈希值的作用是確定該對(duì)象在哈希表中的位置),曾經(jīng)有這樣一句流川千古的話:你必須在每個(gè)重寫equals()的類中重寫一遍hashcode()方法
如果不這樣做將會(huì)違反Object.hashcode()的一般約定,這會(huì)阻止lei與所有基于散列的集合(比如hashmap,hashset…)一起正常工作。為啥?因?yàn)閔ashmap,hashset等基于散列的集合中,會(huì)使用對(duì)象的hashcode值來確定該對(duì)象應(yīng)該如何存儲(chǔ)到集合中,并且再次使用hashcode來定位對(duì)象在集合中的位置
那么
在一個(gè)類中重寫了equals()但沒重寫hashcode()會(huì)出現(xiàn)啥情況呢?
我們來通過一個(gè)例子試試水~
嘗試把對(duì)象都放入一個(gè)不能重復(fù)的set里,然后看集合的長度來判斷兩個(gè)對(duì)象是否相等!
public class equals_hashcode { public static void main(String[] args) { Person person1 = new Person("lyy"); Person person2 = new Person("lyy"); HashSet<Person> set = new HashSet<>(); set.add(person1); set.add(person2); System.out.println(set.size()); } } class Person { public String name; public Person(String name) { this.name = name; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } Person person = (Person) obj; return Objects.equals(name, person.name); } @Override public int hashCode() { return Objects.hash(name); } }
情況一:
當(dāng)只重寫了equals()方法時(shí)運(yùn)行的結(jié)果為2,由此得出兩個(gè)對(duì)象不相等!
情況二:
當(dāng)既重寫了equals()和hashcode()后運(yùn)行結(jié)果為1,所以兩個(gè)對(duì)象相等!
由此得出,對(duì)象相等的本質(zhì)是:
1.地址相同
2.哈希值相同(重寫hashcode的體現(xiàn))
這也不難聯(lián)系到之前的約定了,在比較對(duì)象是否相等的場景下,我們必須重寫equals()和hashcode()!
對(duì)象相等建立在==之上,equals(),hashcode()的雙重寫是對(duì)象相等的基本準(zhǔn)則!
到此這篇關(guān)于Java中==與equals()及hashcode()三者之間的關(guān)系詳解的文章就介紹到這了,更多相關(guān)Java == equals() hashcode()內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java ==,equals()與hashcode()的使用
- 詳解Java中==和equals()的區(qū)別
- java中“==“和equals()的區(qū)別詳解
- 詳解java==運(yùn)算符和equals()方法的區(qū)別
- Java中比較運(yùn)算符compareTo()、equals()與==的區(qū)別及應(yīng)用總結(jié)
- Java中==符號(hào)與equals()的使用詳解(測試兩個(gè)變量是否相等)
- Java中的== 和equals()方法詳解與實(shí)例
- 詳解Java中“==”與equals()的區(qū)別
- Java中的==和equals()區(qū)別小結(jié)
相關(guān)文章
SpringBoot實(shí)現(xiàn)定時(shí)發(fā)送郵件的三種方法案例詳解
這篇文章主要介紹了SpringBoot三種方法實(shí)現(xiàn)定時(shí)發(fā)送郵件的案例,Spring框架的定時(shí)任務(wù)調(diào)度功能支持配置和注解兩種方式Spring?Boot在Spring框架的基礎(chǔ)上實(shí)現(xiàn)了繼承,并對(duì)其中基于注解方式的定時(shí)任務(wù)實(shí)現(xiàn)了非常好的支持,本文給大家詳細(xì)講解,需要的朋友可以參考下2023-03-03在Android的應(yīng)用中實(shí)現(xiàn)網(wǎng)絡(luò)圖片異步加載的方法
這篇文章主要介紹了在Android的應(yīng)用中實(shí)現(xiàn)網(wǎng)絡(luò)圖片異步加載的方法,一定程度上有助于提高安卓程序的使用體驗(yàn),需要的朋友可以參考下2015-07-07Java多線程基礎(chǔ) 線程的等待與喚醒(wait、notify、notifyAll)
這篇文章主要介紹了Java多線程基礎(chǔ) 線程的等待與喚醒,需要的朋友可以參考下2017-05-05Java使用ExecutorService來停止線程服務(wù)
這篇文章主要介紹了Java使用ExecutorService來停止線程服務(wù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04一文詳解如何使用線程池來優(yōu)化我們的應(yīng)用程序
線程池是一種工具,但并不是適用于所有場景。在使用線程池時(shí),我們需要根據(jù)應(yīng)用程序的性質(zhì)、計(jì)算資源的可用性和應(yīng)用程序的需求進(jìn)行適當(dāng)?shù)呐渲?。本文主要介紹了如何使用線程池來優(yōu)化我們的應(yīng)用程序,需要的可以參考一下2023-04-04SpringAOP事務(wù)配置語法及實(shí)現(xiàn)過程詳解
這篇文章主要介紹了SpringAOP事務(wù)配置語法及實(shí)現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06Java實(shí)現(xiàn)Socket的TCP傳輸實(shí)例
這篇文章主要介紹了Java實(shí)現(xiàn)Socket的TCP傳輸,實(shí)例分析了java通過socket實(shí)現(xiàn)TCP傳輸?shù)南嚓P(guān)技巧,需要的朋友可以參考下2015-05-05