解釋一下==和equals的區(qū)別媚媒,你以為就這么簡單?那你就草率了

== 和equals() 比較結(jié)果不同的原因

=======================

對于這個(gè)問題涩僻,可以幫助我們很好的理解Java對象的創(chuàng)建欣范,賦值以及== 和equals()的用法变泄。

我們通過如下實(shí)例來說明,先看一個(gè)簡單的代碼:

public class Practice1 {

public static void main(String[] args) {

String str1=new String("hello");

String str2=new String("hello");

String str3="hello";

String str4="hello"; //結(jié)果為true System.out.println("equals: "+str1.equals(str2));

//1.結(jié)果為true

System.out.println("equals: "+str1.equals(str3));

//2.結(jié)果為false

System.out.println("str==str1 ?"+(str1==str3));

//結(jié)果為true System.out.println("str2==str1 ?"+(str3==str4));

}}

在回答問題之前恼琼,我們需要再來明確某些內(nèi)容妨蛹,它們可以幫助我們更好的理解問題的答案。

前文敘述有點(diǎn)長晴竞,也可先看第三部分蛙卤,如果看完之后還不是很懂,可以回來從此再看噩死。

一颤难、先談創(chuàng)建對象的相關(guān)內(nèi)容

=============

雖然我們都知道Java是面向?qū)ο蟮木幊蹋⒎峭耆拿嫦驅(qū)ο笠盐1热缯fJava中的基本數(shù)據(jù)類型行嗤,用int,double等創(chuàng)建的變量都不是對象垛耳。一般我們都是通過new 關(guān)鍵字來創(chuàng)建對象栅屏,而基本數(shù)據(jù)類型創(chuàng)建的變量并不能用new 的方式獲取。雖然如此堂鲜,但Java對基本數(shù)據(jù)類型也有相應(yīng)的解決辦法——封裝與其相應(yīng)的類栈雳,即Integer對應(yīng)int,Double對應(yīng)double缔莲,它們皆是為了解決基本數(shù)據(jù)類型面向?qū)ο笥玫摹?/p>

明確這些之后哥纫,我們再來看類型是如何分配的,基本數(shù)據(jù)類型在棧中進(jìn)行分配痴奏,而對象類型在堆中進(jìn)行分配蛀骇。基本類型之間的賦值是創(chuàng)建新的拷貝读拆,而對象之間的賦值只是傳遞引用松靡。所有方法的參數(shù)(基本類型除外)傳遞的都是引用而非本身的值。

* * *

現(xiàn)在建椰,再來聊聊String s="hello";以及String s = new String("hello");

我在之前的一篇博客中簡單的提到過String s="hello";(變量&數(shù)據(jù)類型),它與new不同岛马,同時(shí)也是java中唯一不需要new就可以產(chǎn)生對象的途徑棉姐。這種形式的賦值稱為——直接量,它被放在一個(gè)叫作字符串拘留池(常量池)的地方啦逆;而new 創(chuàng)建的對象都放在堆上伞矩。String s="hello" 這種形式的字符串,會(huì)在JVM(Java虛擬機(jī))中發(fā)生字符串拘留夏志。

那什么是字符串拘留呢乃坤?我們通過一個(gè)例子來理解這種機(jī)制,當(dāng)我們聲明一個(gè)字符串String s="hello";時(shí),JVM會(huì)先從常量池中查找有沒有值為"hello"的對象湿诊。如果有狱杰,會(huì)把該對象賦給當(dāng)前引用,也就是說原來的引用和現(xiàn)在的引用指向同一個(gè)對象厅须;如果沒有仿畸,則在拘留池中創(chuàng)建一個(gè)值為"hello"的對象,如果下一次還有類似的語句朗和,例如String str="hello";時(shí)错沽,又會(huì)將str指向"hello"這個(gè)對象。以這種形式聲明字符串眶拉,無論有多少個(gè)都指向同一個(gè)對象千埃。

再來說說String s = new String("hello");

這種形式創(chuàng)建的對象就和其他new 創(chuàng)建的對象一樣了,每執(zhí)行一次就生成一個(gè)新對象忆植,也就是說String str = new String("hello");與s毫無關(guān)系放可,他們是兩個(gè)獨(dú)立的對象,只不過巧了唱逢,他們的值或是說內(nèi)容相等吴侦。

* * *

我們也可以簡單的理解為:

String str = "hello"; 先在內(nèi)存中找是否有"hello"這個(gè)對象,如果有就可以直接從常量池中拿來用坞古,不用再從內(nèi)存中創(chuàng)建空間來存儲(chǔ)备韧。如果沒有,就創(chuàng)建一塊新內(nèi)存存著痪枫,以后要是有對象要用就直接給它用了织堂。

String str=new String ("hello") 就是不管內(nèi)存里有沒有"hello"這個(gè)對象,都新建一個(gè)對象保存"hello"奶陈。

看幾個(gè)例子:

String s1 = "qibao"; // 放在常量池中易阳,沒找到,新建一個(gè) String s2 = "qibao"; // 從常量池中查找吃粒,找到了潦俺,直接引用。s1徐勃,s2指向同一個(gè)對象

String s3 = new String("qibao"); // s3 為一個(gè)引用 String s4 = new String("qibao"); // s4 也是一個(gè)引用事示。雖然s3,s4對象的內(nèi)容一樣僻肖,但他們卻占著兩塊地肖爵。

String s5 = "qi" + "bao"; //字符串常量相加,在編譯時(shí)就會(huì)計(jì)算結(jié)果臀脏,s1 == s5 返回ture

String s6 = "qi"; String s7 = "bao"

String s8 = s6 + s7; //字符串變量相加劝堪,編譯時(shí)無法計(jì)算冀自,s1 == s8 返回false

class Person{

String name;

Person(String name) { this.name = name;}

}

Person p1 = new Person("qibao");

Person p2 = new Person("qibao");

p1.name == p2.name //返回true

* * *

二、再說== 跟equals() 的事

===================

* ? ==

先解釋幾個(gè)名詞:

* ? 寄存器:最快的存儲(chǔ)區(qū), 系統(tǒng)分配,程序中無法控制.

* ? 棧:基本數(shù)據(jù)類型變量和對象引用的存儲(chǔ)區(qū)秒啦,對象本身不放在棧中熬粗,而是存放在堆或者常量池中。

* ? 堆:new創(chuàng)建對象的存儲(chǔ)區(qū)帝蒿。

* ? 靜態(tài)域:靜態(tài)成員變量的存儲(chǔ)區(qū)荐糜。

* ? 常量池:基本數(shù)據(jù)類型常量和字符串常量的存儲(chǔ)區(qū)。

== 或 != 比較的是 棧中存放的對象引用 在堆上的地址葛超, 即判斷兩對象的堆地址是否相同暴氏,即是否是指相同一個(gè)堆對象。 對于基本類型绣张,== 和 != 是比較值答渔。 對于對象來說,== 和 != 是比較兩個(gè)引用,即判斷兩個(gè)對象的地址是否相同.

* ? equals()

我們先來看Object中定義的equals()

public boolean equals(Object obj) {

return (this == obj);

}

Object.equals()使用的算法區(qū)分度高侥涵,只要兩對象不是同一個(gè)就是錯(cuò)誤的沼撕。由于所有的類都繼承自O(shè)bject類,所以equals()適用于所有對象芜飘。Object中的equals方法返回 == 的判斷务豺,即對象的地址判斷。 雖然如此嗦明,但可以對Object.equals()進(jìn)行覆蓋笼沥,String類則實(shí)現(xiàn)覆蓋。我們再來看看String.equals():

private final char value[];

public String(String original) {

this.value = original.value;

this.hash = original.hash;

}public boolean equals(Object anObject) {

if (this == anObject) {

return true;

}if (anObject instanceof String) {

String anotherString = (String)anObject;

int n = value.length;

if (n == anotherString.value.length) {

char v1[] = value;

char v2[] = anotherString.value;

int i = 0;

while (n-- != 0) {

if (v1[i] != v2[i])

return false;i++;

}return true;

}}return false;

}

查看String對equals覆蓋的源碼會(huì)發(fā)現(xiàn)娶牌,String.equals()相等的條件是:比較二者同為String類型奔浅,長度相等,且字符串值完全相同诗良,包括順序和值汹桦,不再要求兩者為同一對象。也可以理解為String.equals()將原本的String對象拆分成單個(gè)字符之間值的比較鉴裹,每個(gè)字符的比較完之后返回一個(gè)最終的boolean類型的值舞骆,即將原本可能指向不同堆地址的兩個(gè)對象 "間接的" 指向了同一個(gè)地址,以到達(dá)比較值的目的。

三径荔、解決問題的時(shí)候到了

===========

看完上邊這些內(nèi)容之后督禽,我們再來看這兩行代碼:

String str1=new String("hello");String str3="hello";//1.結(jié)果為trueSystem.out.println("equals: "+str1.equals(str3));//2.結(jié)果為falseSystem.out.println("str==str1 ?"+(str1==str3));

現(xiàn)在,我們就可以很清楚的知道為什么1 返回true:

String.equal() 只看兩者是否為String猖凛,長度是否相等,以及每個(gè)字符的值是否相等绪穆,很顯然str1和str2滿足這三點(diǎn)要求辨泳,所以返回結(jié)果為真虱岂。

至于2 返回false,我們也明白為何了:

== 是對對象地址的判斷菠红,而這兩種聲明方式的存儲(chǔ)形式是不同的第岖,因此它們的地址不同,自然返回false了试溯。

* * *

重載equals()方法

============

我們在寫程序時(shí)蔑滓,往往有時(shí)Java類庫中的equals方法不能滿足我們的需求。這時(shí)遇绞,就需要我們自己來定義equals方法键袱。

在寫自定義equals方法之前,我們先來看兩個(gè)類庫中已經(jīng)寫好的equals方法摹闽。

一蹄咖、Object.equals()

=================

很簡單的一個(gè)方法,因?yàn)槭荗bject的方法付鹿,所以對所有對象都適用澜汤。

public boolean equals(Object obj) {return (this == obj);}

二、String.equals()

=================

private final char value[];

public String(String original) {

this.value = original.value;

this.hash = original.hash;

}

public boolean equals(Object anObject) {

if (this == anObject) {

return true;

}if (anObject instanceof String) {

String anotherString = (String)anObject;

int n = value.length;

if (n == anotherString.value.length) {

char v1[] = value;

char v2[] = anotherString.value;

int i = 0;

while (n-- != 0) {

if (v1[i] != v2[i])

return false;i++;

}return true;

}}

return false;

}

我們來看String類重寫equals方法時(shí)舵匾,都做了些什么俊抵。

首先是判斷是不是自己,如果是自己好辦坐梯,直接返回true就完事了徽诲。

然后如果不是自己,先看看傳入的參數(shù)是否為String類型烛缔,不是返回false就完事馏段。

再然后都是String類型了,在比較長度是否相等践瓷,每個(gè)字符的值是否相等院喜,全都相等就返回true。

三晕翠、自定義equals()

=============

通過模仿String.equals()喷舀,我們來寫Person.equals()。

class Person {

private String name;

private int age;

public Person(String name, int age) {

this.name = name;

this.age = age;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getAge() {

return age;

} public void setAge(int age) {

this.age = age; }

public boolean equals(Object another) {

//先判斷是不是自己,提高運(yùn)行效率

if (this == another)

return true;

//再判斷是不是Person類,提高代碼的健壯性

if (another instanceof Person) {

//向下轉(zhuǎn)型,父類無法調(diào)用子類的成員和方法

Person anotherPerson = (Person) another;

//最后判斷類的所有屬性是否相等淋肾,其中String類型和Object類型可以用相應(yīng)的equals()來判斷

if ((this.getName().equals(anotherPerson.getName())) && (this.getAge() == anotherPerson.getAge()))

return true;

} else {

return false;

}

return false;

}}}

在覆蓋equals()時(shí)硫麻,我們在自定義equals內(nèi)部調(diào)用了Object.equals()和String.equals()。

四樊卓、自動(dòng)生成的equals()

===============

考慮到實(shí)際中我們可能會(huì)經(jīng)常覆寫equals()拿愧,因此eclipse為我們提供自動(dòng)生成的equals()。

操作過程如上圖所示碌尔,讀者可自行操作查看自動(dòng)生成的代碼浇辜。

個(gè)人公眾號:Java架構(gòu)師聯(lián)盟券敌,每日更新技術(shù)好文

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市柳洋,隨后出現(xiàn)的幾起案子待诅,更是在濱河造成了極大的恐慌,老刑警劉巖熊镣,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卑雁,死亡現(xiàn)場離奇詭異,居然都是意外死亡绪囱,警方通過查閱死者的電腦和手機(jī)测蹲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來毕箍,“玉大人弛房,你說我怎么就攤上這事《蹋” “怎么了文捶?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長媒咳。 經(jīng)常有香客問我粹排,道長,這世上最難降的妖魔是什么涩澡? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任顽耳,我火速辦了婚禮,結(jié)果婚禮上妙同,老公的妹妹穿的比我還像新娘射富。我一直安慰自己,他們只是感情好粥帚,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布胰耗。 她就那樣靜靜地躺著,像睡著了一般芒涡。 火紅的嫁衣襯著肌膚如雪柴灯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天费尽,我揣著相機(jī)與錄音赠群,去河邊找鬼。 笑死旱幼,一個(gè)胖子當(dāng)著我的面吹牛查描,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼冬三,長吁一口氣:“原來是場噩夢啊……” “哼鸯两!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起长豁,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎忙灼,沒想到半個(gè)月后匠襟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡该园,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年酸舍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片里初。...
    茶點(diǎn)故事閱讀 39,722評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡啃勉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出双妨,到底是詐尸還是另有隱情淮阐,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布刁品,位于F島的核電站泣特,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏挑随。R本人自食惡果不足惜状您,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望兜挨。 院中可真熱鬧膏孟,春花似錦、人聲如沸拌汇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽担猛。三九已至幕垦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間傅联,已是汗流浹背先改。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蒸走,地道東北人仇奶。 一個(gè)月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親该溯。 傳聞我的和親對象是個(gè)殘疾皇子岛抄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評論 2 353