Effective Java 讀書(shū)筆記(2)

Object 通用方法

Object是一個(gè)具體類(lèi)窿侈,但是設(shè)計(jì)它主要是為了擴(kuò)展,其所有的非final方法(equals淤井、hashCode、toString出爹、clone庄吼、finalize)都有明確的通用約定,我們可以在遵守通用約定的前提下override這些方法严就。

注:不遵守通用約定(general contract)復(fù)寫(xiě)Object類(lèi)的非final方法,可以會(huì)導(dǎo)致依賴(lài)于這些約定的類(lèi)(hashMap之類(lèi)的)無(wú)法正常工作器罐。


第8條:覆蓋equals時(shí)請(qǐng)遵守通用約定

Object的equals原意:類(lèi)的實(shí)例只與它本身相同梢为,即比較地址相同,不比較值轰坊。
在以下任意情況下不應(yīng)覆蓋equals方法:

  • 類(lèi)的每個(gè)實(shí)例本質(zhì)上都是唯一的铸董,即比較地址不比較值(如Thread)
  • 不關(guān)心類(lèi)是否提供了“邏輯相等(值相等)”的測(cè)試功能
  • 超類(lèi)已覆蓋了equals方法,且適用于子類(lèi)(如Set實(shí)現(xiàn)類(lèi)從AbstractSet繼承equals方法肴沫,List實(shí)現(xiàn)類(lèi)從AbstractList繼承equals方法)
  • 類(lèi)是私有的或是包級(jí)私有的粟害,可以確定其equals不會(huì)被調(diào)用。

Tip:禁止調(diào)用可以在對(duì)應(yīng)的方法體內(nèi)拋出錯(cuò)誤throw new AssertionError()

如果類(lèi)具有其特有的“邏輯相等”概念颤芬。并且超類(lèi)沒(méi)有實(shí)現(xiàn)期待的行為悲幅,此時(shí)我們需要覆蓋equals方法。這通常屬于“值類(lèi)(value class)”的情況站蝠。值類(lèi)僅僅是一個(gè)表示值的類(lèi)汰具,如Integer或者Date。當(dāng)我們調(diào)用值類(lèi)的equals時(shí)菱魔,往往是為了判斷其值是否相等留荔,而不關(guān)心是否為同一個(gè)對(duì)象(可以使用==判斷是否為同一實(shí)例)。

Tip: Set澜倦、List聚蝶、Map等集合類(lèi)多使用equals判斷key是否相等,所以我們需要在使用這些類(lèi)時(shí)確保我們的equals方法是符合我們的預(yù)期的藻治。

equals方法的通用約定(JavaSE6):
x碘勉、y、z皆為非null引用值

  • 自反性(reflextive):assert x.equals(x);
  • 對(duì)稱(chēng)性(symmetric):if(x.equals(y)) assert y.equals(x);
  • 傳遞性(transitive):if(x.equals(y)&&y.equals(z)) assert x.equals(z);
  • 一致性(consistent):如果x栋艳、y未被修改恰聘,則調(diào)用x.equals(y)應(yīng)一直返回true或者一直返回false
  • 非空性(Non-nullify):x.equals(null)必須返回false,assert !x.equals(null)

實(shí)現(xiàn)高質(zhì)量equals的訣竅:

  • 使用==操作符檢查“參數(shù)是否為這個(gè)對(duì)象的引用”
  • 使用instanceof操作符檢查“參數(shù)是否為正確的類(lèi)型”
  • 把參數(shù)轉(zhuǎn)換為正確的類(lèi)型
  • 對(duì)于該類(lèi)的“關(guān)鍵域”,逐一比較(見(jiàn)以下Tip)
  • 回顧并進(jìn)行單元測(cè)試:對(duì)稱(chēng)性晴叨、傳遞性凿宾、一致性

Tip: 逐一比較的訣竅:
1、對(duì)于不是float兼蕊、double的基本類(lèi)型域初厚,可以直接使用==進(jìn)行比較;
2孙技、對(duì)于對(duì)象引用域产禾,可以遞歸地調(diào)用equals方法進(jìn)行比較;
3牵啦、對(duì)于float域亚情,可以使用Float.compare方法;對(duì)于double域哈雏,可以使用Double.compare方法楞件;
4、對(duì)于數(shù)組域裳瘪,可以使用Array.equals方法
5土浸、使用field == null ? o.field == null : field.equals(o.field)(field == o.field) || (field != null) && (field.equals(o.field))習(xí)慣寫(xiě)法
6、優(yōu)化比較順序:優(yōu)先比較最有可能不一致的域彭羹、比較開(kāi)銷(xiāo)最低的域黄伊。不比較不屬于對(duì)象邏輯狀態(tài)(值)的域,或者冗余域(除非其能代表大量關(guān)鍵域派殷,比較其能節(jié)省大量時(shí)間)

最后的告誡:

  • 覆蓋equals時(shí)總要覆蓋hashCode(見(jiàn)第9條)还最;
  • 不要企圖讓equals過(guò)于智能;
  • 不要將equals聲明中的Object對(duì)象替換為其他的類(lèi)型(這樣是overload而不是override)愈腾,可以添加@override注解幫助規(guī)避錯(cuò)誤憋活。

第9條:覆蓋equals時(shí)總要覆蓋hashCode

在Object類(lèi)中,hashCode方法是一個(gè)native方法虱黄,返回值與對(duì)象的存儲(chǔ)地址相關(guān)悦即,計(jì)算方法由jvm決定。

hashCode通用公約(JavaSE6):

  • 在應(yīng)用程序的執(zhí)行期間橱乱,只要對(duì)象的equals方法的比較操作所用到的信息沒(méi)有被修改辜梳,那么對(duì)這同一個(gè)對(duì)象調(diào)用多次,hashCode方法都必須始終如一地返回同一個(gè)整數(shù)泳叠。在同一個(gè)程序多次執(zhí)行過(guò)程中作瞄,每次執(zhí)行所返回的整數(shù)可以不一致;
  • 如果兩個(gè)對(duì)象根據(jù)equals方法比較是相等的危纫,那么調(diào)用者兩個(gè)對(duì)象的hashCode方法必須返回同一個(gè)整數(shù)宗挥;
  • 如果兩個(gè)對(duì)象根據(jù)equals方法比較是不相等的乌庶,那么調(diào)用者兩個(gè)對(duì)象的hashCode方法不一定返回不同整數(shù)。給不相等的對(duì)象產(chǎn)生截然不同的整數(shù)結(jié)果契耿,有可能提高散列表(hash table)的性能瞒大;

注: HashTable、HashMap搪桂、HashSet等散列集合類(lèi)的散列算法實(shí)現(xiàn)往往依賴(lài)于hashCode透敌。假設(shè)我們覆蓋了equals方法,但沒(méi)有覆蓋hashCode踢械,那么在我們理解中equals的對(duì)象酗电,對(duì)于散列集合類(lèi)而言,可能是不相等内列。

高質(zhì)量的hashCode方法的原則:

  • 對(duì)于equals的對(duì)象撵术,返回同一個(gè)整數(shù);
  • 對(duì)于非equals的對(duì)象话瞧,盡可能返回不一樣的整數(shù)荷荤;
  • 為不相等的對(duì)象均勻產(chǎn)生不相等的散列碼。

編寫(xiě)好質(zhì)量的hashCode的訣竅:

  • 把某個(gè)非零的常數(shù)值(如17)保存在一個(gè)名為result的int類(lèi)型的變量中移稳;
  • 對(duì)于對(duì)象中的每個(gè)關(guān)鍵域f,完成以下步驟:
    1会油、為該域計(jì)算int類(lèi)型的散列碼c(見(jiàn)以下Tip)个粱;
    2、計(jì)算result = 31 * result +c翻翩,合并c
  • 返回result
  • 編寫(xiě)單元測(cè)試都许,保證相等的實(shí)例能得到同一個(gè)散列值

TIp: 計(jì)算int類(lèi)型的散列碼c
1、如果該域是boolean類(lèi)型嫂冻,則計(jì)算f ? 1 : 0胶征;
2、如果該域是byte桨仿、char睛低、short、int類(lèi)型服傍,則計(jì)算(int) f钱雷;
3、如果該域是long類(lèi)型吹零,則計(jì)算(int)( f ^ (f>>>32))罩抗;
4、如果該域是float類(lèi)型灿椅,則計(jì)算Float.floatToTintBits(f)套蒂;
5钞支、如果該域是long類(lèi)型,則計(jì)算'Double.doubleToLongBits(f)'操刀,然后按照步驟3為long計(jì)算散列值烁挟;
6、如果該域是一個(gè)對(duì)象引用馍刮,為null則返回0信夫,否則遞歸調(diào)用hashCode;
7卡啰、如果該域是一個(gè)數(shù)組静稻,調(diào)用Arrays.hashCode

在計(jì)算散列碼時(shí)匈辱,只能用到覆蓋的equals函數(shù)比較的關(guān)鍵域振湾,否則相等(equals)的對(duì)象可以會(huì)得到不同的散列值。
初始化常數(shù)值17是任選的亡脸,不為0就可以了押搪,目的在于增加散列值為0的關(guān)鍵域的影響。
31是一個(gè)比較特殊的數(shù)字浅碾,首先它是一個(gè)奇素?cái)?shù)大州,如果乘數(shù)為偶數(shù),則相當(dāng)于移位垂谢,會(huì)增加沖突厦画。再者,其乘法可以被jvm自動(dòng)優(yōu)化:31 * i == (i << 5 ) - i滥朱。

Tip:如果一個(gè)對(duì)象是不可變的根暑,或者其散列值計(jì)算開(kāi)銷(xiāo)比較大,可以考慮將其散列值在實(shí)例初始化時(shí)計(jì)算后緩存在對(duì)象內(nèi)部徙邻。


第10條:始終要覆蓋toString

在Object類(lèi)中排嫌,toString會(huì)返回類(lèi)名,一個(gè)@符號(hào)缰犁,和散列碼的無(wú)符號(hào)十六進(jìn)制表示法淳地,這通常不是我們所希望看到的。
toString的通用約定指出民鼓,被返回的字符串應(yīng)該是一個(gè)簡(jiǎn)潔的薇芝,信息豐富的,易于閱讀的表達(dá)形式丰嘉,并建議所有的子類(lèi)都o(jì)verride這個(gè)方法夯到。
提供好的toString實(shí)現(xiàn)可以使類(lèi)用起來(lái)更加舒適,特別是在打印對(duì)象信息的時(shí)候饮亏,我們可以之間將對(duì)象作為參數(shù)直接傳入print函數(shù)耍贾,打印出對(duì)象信息阅爽。
在實(shí)際應(yīng)用中,toString方法應(yīng)該返回對(duì)象中包含的所有值得關(guān)注的信息
在實(shí)現(xiàn)toString時(shí)荐开,還應(yīng)考慮是否在文檔中指定返回值的格式付翁。若是指定格式,最好再提供一個(gè)靜態(tài)工廠(chǎng)方法或者構(gòu)造器晃听,以便從這種表示法中轉(zhuǎn)換對(duì)象百侧;若是不指定返回值的格式,則可以保持toString方法的靈活性能扒,以便在后期改進(jìn)格式佣渴。


第11條:謹(jǐn)慎覆蓋clone方法

Cloneable接口的目的在于表明這個(gè)對(duì)象是允許被clone的,然而它并沒(méi)有成功地達(dá)到這個(gè)目的初斑。其主要原因在于它并沒(méi)有包含任何方法辛润,而Object的clone方法是受保護(hù)的,需要反射調(diào)用见秤,而且反射調(diào)用也不一定會(huì)成功砂竖。
Cloneable接口的作用在于表明:實(shí)現(xiàn)了Cloneable接口的類(lèi),應(yīng)該覆蓋了Object的clone方法鹃答。
clone方法的通用公約:
創(chuàng)建和返回該對(duì)象的一個(gè)拷貝乎澄。這個(gè)拷貝的精確含義由該對(duì)象的類(lèi)決定。一般有以下含義:

  • 對(duì)于任何對(duì)象x测摔,表達(dá)式x.clone() != x將會(huì)是true
  • 對(duì)于任何對(duì)象x三圆,表達(dá)式x.clone().getClass() == x.getClass將會(huì)是true
  • 對(duì)于任何對(duì)象x,表達(dá)式x.clone().equals(x)將會(huì)是true

拷貝對(duì)象往往會(huì)導(dǎo)致創(chuàng)建它的類(lèi)的一個(gè)新實(shí)例避咆,但它同時(shí)要求拷貝類(lèi)內(nèi)部的數(shù)據(jù)結(jié)構(gòu)。這個(gè)過(guò)程沒(méi)有調(diào)用構(gòu)造器修噪。

覆蓋clone方法查库,必須保證其拷貝是深度拷貝,這往往需要其超類(lèi)實(shí)現(xiàn)了良好的clone方法黄琼。其次樊销,對(duì)于拷貝對(duì)象內(nèi)部的引用對(duì)象,我們需要實(shí)例化一個(gè)新的對(duì)象脏款,并修改其值围苫,而不能簡(jiǎn)單復(fù)制引用。覆蓋clone方法是一件十分吃力不討好的事撤师。
所有實(shí)現(xiàn)了Cloneable接口的類(lèi)都應(yīng)該用一個(gè)公有的方法覆蓋clone剂府。此公有方法首先調(diào)用super.clone(),然后修正任何需要修正的域。
除非你擴(kuò)展了一個(gè)實(shí)現(xiàn)Cloneable接口的類(lèi)剃盾,否則最好提供某些其他的途徑代替clone方法腺占,或者干脆不提供這樣的功能淤袜。
我們可以提供一個(gè)拷貝構(gòu)造器或者拷貝工廠(chǎng)來(lái)替代clone方法:

// 拷貝構(gòu)造器:以拷貝對(duì)象為參數(shù)
public Yum(Yum yum)
// 拷貝工廠(chǎng)
public static Yum newInstance(Yum yum)

更進(jìn)一步,我們可以提供一個(gè)轉(zhuǎn)換構(gòu)造器衰伯,其參數(shù)是一個(gè)超類(lèi)/接口的對(duì)象铡羡。按照慣例,所有通用集合實(shí)現(xiàn)都提供了一個(gè)轉(zhuǎn)換構(gòu)造器意鲸,如一個(gè)HashSet s,可以通過(guò)TreeSet(s)轉(zhuǎn)換類(lèi)型烦周。

注意:對(duì)于一個(gè)專(zhuān)門(mén)為了繼承而設(shè)計(jì)的類(lèi),如果你未能提供香味良好的受保護(hù)的clone方法怎顾,它的子類(lèi)就不可能實(shí)現(xiàn)Cloneable方法读慎。


第12條:考慮實(shí)現(xiàn)Comparable接口

compareTo方法是Comparable接口唯一的方法,它與Object類(lèi)的通用方法有很大的相似性杆勇。compareTo方法不但允許進(jìn)行簡(jiǎn)單的等同性比較贪壳,而且允許執(zhí)行順序比較。類(lèi)實(shí)現(xiàn)了Comparable接口蚜退,就表明它的實(shí)例具有內(nèi)在的排序關(guān)系闰靴。對(duì)于實(shí)現(xiàn)了Comparable接口的對(duì)象數(shù)組進(jìn)行排序:Arrays.sort(a)
一旦類(lèi)實(shí)現(xiàn)了Comparable接口,它就可以跟許多泛型算法以及依賴(lài)于該接口的集合實(shí)現(xiàn)進(jìn)項(xiàng)協(xié)作钻注。事實(shí)上蚂且,java的所有公共值類(lèi)都實(shí)現(xiàn)了這個(gè)接口。假設(shè)你正在編寫(xiě)一個(gè)值類(lèi)幅恋,它具有非常明顯的內(nèi)在排序關(guān)系杏死,那么你就應(yīng)該堅(jiān)決考慮實(shí)現(xiàn)這個(gè)接口。

compareTo的通用公約:
將這個(gè)對(duì)象與指定的對(duì)象進(jìn)行比較捆交,當(dāng)對(duì)象

  • 小于指定對(duì)象時(shí)淑翼,返回負(fù)整數(shù);
  • 等于指定對(duì)象時(shí)品追,返回0玄括;
  • 大于指定對(duì)象時(shí),返回正整數(shù)肉瓦;
  • 無(wú)法與指定對(duì)象進(jìn)行比較遭京,拋出ClassCastException

與equals類(lèi)似,compareTo同樣有自反型泞莉、傳遞性哪雕、對(duì)稱(chēng)性
編寫(xiě)compareTo方法與編寫(xiě)equals方法非常相似,但也有一些區(qū)別:

  • 不用類(lèi)型檢查與轉(zhuǎn)換鲫趁,compareTo方法的參數(shù)是靜態(tài)類(lèi)型斯嚎,不是Object
  • 當(dāng)參數(shù)為null時(shí),應(yīng)該拋出NullPointException
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市孝扛,隨后出現(xiàn)的幾起案子列吼,更是在濱河造成了極大的恐慌,老刑警劉巖苦始,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寞钥,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡陌选,警方通過(guò)查閱死者的電腦和手機(jī)理郑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)咨油,“玉大人您炉,你說(shuō)我怎么就攤上這事∫鄣纾” “怎么了赚爵?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)法瑟。 經(jīng)常有香客問(wèn)我冀膝,道長(zhǎng),這世上最難降的妖魔是什么霎挟? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任窝剖,我火速辦了婚禮,結(jié)果婚禮上酥夭,老公的妹妹穿的比我還像新娘赐纱。我一直安慰自己,他們只是感情好熬北,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布疙描。 她就那樣靜靜地躺著,像睡著了一般讶隐。 火紅的嫁衣襯著肌膚如雪淫痰。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,736評(píng)論 1 312
  • 那天整份,我揣著相機(jī)與錄音,去河邊找鬼籽孙。 笑死烈评,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的犯建。 我是一名探鬼主播讲冠,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼适瓦!你這毒婦竟也來(lái)了竿开?” 一聲冷哼從身側(cè)響起谱仪,我...
    開(kāi)封第一講書(shū)人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎否彩,沒(méi)想到半個(gè)月后疯攒,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡列荔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年敬尺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贴浙。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡砂吞,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出崎溃,到底是詐尸還是另有隱情蜻直,我是刑警寧澤,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布袁串,位于F島的核電站概而,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏般婆。R本人自食惡果不足惜到腥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蔚袍。 院中可真熱鬧乡范,春花似錦、人聲如沸啤咽。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)宇整。三九已至瓶佳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鳞青,已是汗流浹背霸饲。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留臂拓,地道東北人厚脉。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像胶惰,于是被迫代替她去往敵國(guó)和親傻工。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類(lèi)相關(guān)的語(yǔ)法中捆,內(nèi)部類(lèi)的語(yǔ)法鸯匹,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法泄伪,線(xiàn)程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,666評(píng)論 18 399
  • 文章作者:Tyan博客:noahsnail.com | CSDN | 簡(jiǎn)書(shū) CHAPTER3 Method...
    SnailTyan閱讀 736評(píng)論 1 4
  • 第三章講了一些通用的方法殴蓬。看的時(shí)候很快臂容,記筆記的時(shí)候慢了 0x01 equals方法應(yīng)該如何寫(xiě) equals 方法...
    MaxZing閱讀 330評(píng)論 0 0
  • java筆記第一天 == 和 equals ==比較的比較的是兩個(gè)變量的值是否相等科雳,對(duì)于引用型變量表示的是兩個(gè)變量...
    jmychou閱讀 1,504評(píng)論 0 3
  • 對(duì)象的創(chuàng)建與銷(xiāo)毀 Item 1: 使用static工廠(chǎng)方法,而不是構(gòu)造函數(shù)創(chuàng)建對(duì)象:僅僅是創(chuàng)建對(duì)象的方法脓杉,并非Fa...
    孫小磊閱讀 1,996評(píng)論 0 3