《Effective Java》學(xué)習(xí)筆記上

第2章 創(chuàng)建和銷毀對(duì)象

第1條:考慮用靜態(tài)工廠方法代替構(gòu)造方法

靜態(tài)工廠方法與構(gòu)造方法的不同

優(yōu)點(diǎn):

  • 靜態(tài)工廠方法有名稱。當(dāng)一個(gè)類需要多個(gè)帶有相同簽名的構(gòu)造器時(shí),就用靜態(tài)工廠方法代替構(gòu)造方法,并且慎重選擇名稱以便突出區(qū)別。
  • 不用每次調(diào)用它們的時(shí)候都創(chuàng)建一個(gè)新對(duì)象。靜態(tài)工廠方法能夠?yàn)橹貜?fù)的調(diào)用返回相同的對(duì)象,有助于類總能?chē)?yán)格控制在某個(gè)時(shí)刻那些實(shí)例不該存在。
  • 可以返回原返回類型的任何子類型的對(duì)象。有更大的靈活性;如APi可以返回對(duì)象,又同時(shí)不會(huì)使對(duì)象的類變成公有的,適用于基于接口的框架。公有的靜態(tài)工廠方法返回的對(duì)象的類不僅可以是非公有的,而且該類還可以隨著每次調(diào)用而發(fā)生變化,這取決于靜態(tài)工廠方法的參數(shù)值。
  • 在創(chuàng)建參數(shù)化類型實(shí)例的時(shí)候,代碼更簡(jiǎn)潔。

缺點(diǎn):

  • 類如果不含有公有的或者受保護(hù)的構(gòu)造器,就不能被子類化。
  • 他們與其他的靜態(tài)方法實(shí)際上沒(méi)有任何區(qū)別。

第2條:遇到多個(gè)構(gòu)造器參數(shù)時(shí)要考慮用構(gòu)造器

與構(gòu)造器相比,builder可以有多個(gè)可變的參數(shù)。builder模式十分靈活,可以利用單個(gè)builder構(gòu)造多個(gè)對(duì)象??傊绻惖臉?gòu)造器和靜態(tài)工廠中具有多個(gè)參數(shù),builder模式就是不錯(cuò)的選擇,特別是當(dāng)大多數(shù)參數(shù)是可選的時(shí)候。

第3條:用私有構(gòu)造器或者枚舉類型強(qiáng)化singleton屬性

第4條:用私有構(gòu)造器強(qiáng)化不可實(shí)例化的能力

第5條:避免創(chuàng)建不必要的對(duì)象

一般來(lái)說(shuō),最好能重用對(duì)象而不是在每次需要的時(shí)候就創(chuàng)建一個(gè)相同功能的對(duì)象。

第6條:消除過(guò)期的對(duì)象引用

  • 清空對(duì)象引用應(yīng)該是一種例外,而不是一種規(guī)范行為,消除過(guò)期引用最好的方法是讓包含該引用的變量結(jié)束其生命周期。
  • stack類自己管理內(nèi)存,只要是類自己管理內(nèi)存,程序猿就應(yīng)該警惕內(nèi)存泄漏問(wèn)題。
  • 內(nèi)存泄漏的另一種常見(jiàn)來(lái)源是緩存。只要在緩存之外存在對(duì)某個(gè)項(xiàng)的鍵的引用,該項(xiàng)就有意義,那么就可以用weakhashmap代表緩存。
  • 內(nèi)存泄漏的第三個(gè)常見(jiàn)來(lái)源是監(jiān)聽(tīng)器和其他回調(diào)。確保回調(diào)立即被當(dāng)做垃圾回收的最佳方法是只保存它們的弱引用,例如,只將它們保持成WeakHashMap中的鍵。

第7條:避免使用終結(jié)方法

  • 終結(jié)方法(finalizer)通常是不可預(yù)測(cè)的,也是很危險(xiǎn)的,一般情況下是不必要的。
  • java語(yǔ)言規(guī)范不僅不保證終結(jié)方法會(huì)被及時(shí)執(zhí)行,而且根本不保證它們會(huì)被執(zhí)行。
  • 使用終結(jié)方法有一個(gè)非常嚴(yán)重的(severe)性能損失
  • 顯示的終結(jié)方法通常與try-finally結(jié)構(gòu)結(jié)合起來(lái)使用,以確保及時(shí)終止。(顯示終止方法的典型例子是InputStream,OutputStream和java.sql.Connection上的close方法,java.util.Timer上的cancel方法等)
  • 總之,除非作為安全網(wǎng)(當(dāng)對(duì)象的所有者忘記調(diào)用前面段落中建議的顯示終止方法時(shí),終結(jié)方法可以充當(dāng)安全網(wǎng)),或者是為了終止非關(guān)鍵的本地資源,否則請(qǐng)不要使用終結(jié)方法。

第3章 對(duì)于所有對(duì)象都通用的方法

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

  • 類的每個(gè)實(shí)例本質(zhì)都是唯一的。對(duì)于代表活動(dòng)實(shí)體而不是值的類來(lái)說(shuō)確實(shí)如此,例如Thread。
  • 不關(guān)心類是否提供了“邏輯相等”的測(cè)試功能。
  • 超類已經(jīng)覆蓋了equals,從超類繼承過(guò)來(lái)的行為對(duì)于子類也是合適的。
  • 類是私有的或是包級(jí)私有的,可以確定它的equals方法永遠(yuǎn)不會(huì)被調(diào)用。

在實(shí)現(xiàn)equals方法時(shí),必須遵守的通用約定

  • 自反性。對(duì)于任何非null的引用值x,x.equals(x)必須返回true。
  • 對(duì)稱性。對(duì)于任何非null的引用值x和y,當(dāng)且僅當(dāng)y.equals(x)時(shí),x.equals(y)必須返回true。
  • 傳遞性。對(duì)于任何非null的引用值x,y和z,如果x.equals(y)返回true,并且y.equals(x)也返回true,那么x.equals(z)也必須返回true。
  • 一致性。對(duì)于任何非null的引用值x和y,只要equals的比較操作在對(duì)象中所用的信息沒(méi)有被修改,多次調(diào)用x.equals(y)就會(huì)一致的返回true,或者一致的返回false。
  • 非空性。意思是所有的對(duì)象都必須不等于null。

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

  1. 使用==操作符檢查“參數(shù)是否為這個(gè)對(duì)象的引用”。
  2. 使用instanceof操作符檢查“參數(shù)是否為正確的類型”。
  3. 吧參數(shù)轉(zhuǎn)換成正確的類型。
  4. 對(duì)于該類中的每個(gè)“關(guān)鍵(significant)”域,檢查參數(shù)中的域是否與該對(duì)象中對(duì)應(yīng)的域相匹配。
  5. 當(dāng)編寫(xiě)完成了equals方法之后,應(yīng)該問(wèn)自己三個(gè)問(wèn)題:它是是否對(duì)稱的,傳遞的,一致的?
  6. 覆蓋equals是總要覆蓋hashCode。
  7. 不要企圖讓equals方法過(guò)于智能。
  8. 不要將equals聲明中的Object對(duì)象替換為其他類型。

第9條 覆蓋equals是總要覆蓋hashCode

  • 相等的對(duì)象必須具有相等的散列碼(hash code)
  • 不要試圖從散列碼計(jì)算中出掉一個(gè)對(duì)象的關(guān)鍵部分來(lái)提高性能。

第10條 始終要覆蓋toString

  • 提供好的toString實(shí)現(xiàn)可以使類用起來(lái)更加舒適。
  • toString方法應(yīng)該返回對(duì)象中包含的所有值得關(guān)注的信息。
  • 指定toString返回值的格式也有不足之處:如果這個(gè)類已經(jīng)被廣泛使用,一旦指定格式,就必須始終如一地堅(jiān)持這種格式。
  • 無(wú)論是否決定指定格式,都應(yīng)該在文檔中明確地表明你的意圖。如果要指定格式,則應(yīng)該嚴(yán)格地去做。
  • 無(wú)論是否指定格式,都為toString返回值中包含的所有信息,提供一種編程式的訪問(wèn)途徑。

第11條 謹(jǐn)慎地覆蓋clone

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

第4章 類和接口

第13條:是類和成員的可訪問(wèn)性最小化

  • 區(qū)別設(shè)計(jì)良好的模塊和設(shè)計(jì)不好的模塊,最重要的因素在于,這個(gè)模塊對(duì)于外部的其他模塊而言,是否隱藏其內(nèi)部數(shù)據(jù)和其他實(shí)現(xiàn)細(xì)節(jié)。(信息隱藏/封裝)
  • 盡可能地使每個(gè)類或者成員不被外界訪問(wèn)
  1. 私有的(private)
  2. 包級(jí)私有的(package-private/default)
  3. 受保護(hù)的(protected)
  4. 公有的(public)
  • 如果方法覆蓋了超類中的一個(gè)方法,子類中的訪問(wèn)級(jí)別就不允許低于超類的訪問(wèn)級(jí)別。接口中的所有方法都隱含著公有訪問(wèn)級(jí)別。
  • 實(shí)例域決不能是公有的,包含公有可變域的類并不是線程安全的。
  • 長(zhǎng)度非零的數(shù)組總是可變的,所以類具有公有的靜態(tài)final數(shù)組域,或者返回這種域的訪問(wèn)方法,這幾乎總是錯(cuò)誤的。
  • 總之,應(yīng)該始終盡可能地降低可訪問(wèn)性。除了公有靜態(tài)final域的特殊情況之外,公有類都不應(yīng)該包含公有域。并且要確保公有靜態(tài)final域所引用的對(duì)象都是不可變的。

第14條:在公有類中使用訪問(wèn)方法而非公有域

  • 如果類可以在它所在的包的外部進(jìn)行訪問(wèn),就提供訪問(wèn)方法,以保留將來(lái)改變?cè)擃惖膬?nèi)部表示法的靈活性。
  • 如果類是包級(jí)私有的,或者是私有的嵌套類,直接暴露它的數(shù)據(jù)域并沒(méi)有本質(zhì)的錯(cuò)誤。
  • 公有類永遠(yuǎn)都不應(yīng)該暴露可變域,雖然還是有問(wèn)題,但是讓公有類暴露不可變的域其危害比較小。但是,有時(shí)候會(huì)需要用包級(jí)私有的或者私有的嵌套類來(lái)暴露域,無(wú)論這個(gè)類是可變還是不可變的。

第15條:使可變性最小化

不可變類知識(shí)其實(shí)力不能被修改的類。每個(gè)實(shí)例中包含的所有信息都必須在創(chuàng)建實(shí)例的時(shí)候提供,并在對(duì)象的整個(gè)周期內(nèi)固定不變。

  1. 不要提供任何會(huì)修改對(duì)象狀態(tài)的方法(mutator)
  2. 保證類不會(huì)被擴(kuò)展
  3. 使所有的域都是final的
  4. 使所有的域都成為私有的
  5. 確保對(duì)于任何可變組件的互斥訪問(wèn)(如果類具有指向可變的域,則必須確保該類的客戶端無(wú)法獲得指向這些對(duì)象的引用)
  • 創(chuàng)建并返回新的實(shí)例,而不是修改這個(gè)實(shí)例。大多數(shù)重要的不可變類都使用了這個(gè)模式,被稱為函數(shù)的做法。
  • 不可變對(duì)象本質(zhì)上是線程安全的,它們不要求同步,可以被自由地共享。不僅可以共享不可變對(duì)象,甚至也可以共享它們的內(nèi)部信息,不需要保護(hù)性拷貝。缺點(diǎn):對(duì)于每個(gè)不同的值都需要一個(gè)單獨(dú)的對(duì)象。
  • 總之,堅(jiān)決不用為每個(gè)get方法編寫(xiě)對(duì)應(yīng)的set方法,除非有很好的理由要讓類成為可變類,否則應(yīng)該是不可變的。如果類不能被做成不可變的,仍然應(yīng)改盡可能地限制它的可變性。

第16條:復(fù)合優(yōu)先于繼承

  • 于方法調(diào)用不同,繼承打破了封裝性。
  • 不用擴(kuò)展現(xiàn)有的類,而是在新的類中增加一個(gè)私有域,它引用現(xiàn)有類的一個(gè)實(shí)例。(復(fù)合/轉(zhuǎn)發(fā))這樣得到的類將會(huì)非常穩(wěn)固,它不依賴于現(xiàn)有類的實(shí)現(xiàn)細(xì)節(jié)。
  • 只有當(dāng)子類真正是超類的子類型時(shí)(存在“is-a”關(guān)系),才適合用繼承。

第17條:要么為繼承設(shè)計(jì),并提供文檔說(shuō)明,要么就禁止繼承

第18條:接口優(yōu)于抽象類

  • 接口和抽象類的區(qū)別:抽象類允許包含某些方法的實(shí)現(xiàn),但接口不允許。為了實(shí)現(xiàn)有抽象類定義的類型,類必須成為抽象類的一個(gè)子類,Java只允許單繼承,所以,抽象類作為類型定義受到了極大的限制。
  1. 現(xiàn)有的類可以很容易被更新,以實(shí)現(xiàn)新的接口。
  2. 接口的定義mixin(混合類型)的理想選擇。(mixin允許任選的功能可被混合到類型的主要功能中)
  3. 接口允許我們構(gòu)造非層次結(jié)構(gòu)的類型框架。
  • 通過(guò)對(duì)你導(dǎo)出的每個(gè)重要的接口都提供一個(gè)抽象的骨架實(shí)現(xiàn)(skeletal implementation)類,把接口和抽象類的優(yōu)點(diǎn)都集合起來(lái)。接口的作用仍然是定義類型,但是骨架實(shí)現(xiàn)類接管了所有與接口實(shí)現(xiàn)相關(guān)的工作。
  1. 按照慣例,骨架實(shí)現(xiàn)被稱為AbstractInterface,這里的interface是指所實(shí)現(xiàn)的接口的名字。例如AbstractCollection,AbstractSet,AbstractList和AbstractMap等。
  2. 如果設(shè)計(jì)得當(dāng),骨架實(shí)現(xiàn)可以使程序員很容易提供他們自己的接口實(shí)現(xiàn)。
  3. 骨架實(shí)現(xiàn)為抽象類提供了實(shí)現(xiàn)上的幫助,但又不強(qiáng)加“抽象類被用作類型定義時(shí)”所特有的嚴(yán)格限制。
  4. 骨架實(shí)現(xiàn)類仍然能夠有助于接口的實(shí)現(xiàn)。(模擬多重繼承:實(shí)現(xiàn)這個(gè)接口的類可以把對(duì)于接口方法的調(diào)用,轉(zhuǎn)發(fā)到一個(gè)內(nèi)部私有類的實(shí)例上,這個(gè)內(nèi)部私有類擴(kuò)展了骨架的實(shí)現(xiàn))
  5. 編寫(xiě)骨架實(shí)現(xiàn)類相對(duì)比較簡(jiǎn)單,但是有點(diǎn)單調(diào)乏味。
  6. 因?yàn)楣羌軐?shí)現(xiàn)類是為繼承目的實(shí)現(xiàn)的,所以應(yīng)該遵循第17條中介紹的所有關(guān)于設(shè)計(jì)和文檔的指導(dǎo)原則。
  7. 骨架實(shí)現(xiàn)上有個(gè)小小的不同,就是簡(jiǎn)單實(shí)現(xiàn)(simple implementation),AbstractMap的SimpleEntry就是個(gè)例子。
  • 使用抽象類來(lái)定義允許多個(gè)實(shí)現(xiàn)的類型,與使用接口相比有一個(gè)明顯的優(yōu)勢(shì):抽象類的演變比接口的演變要容易得多。
  • 一般來(lái)說(shuō),要想在公有接口中增加方法,而不破壞實(shí)現(xiàn)這個(gè)接口的所有現(xiàn)有的類,這是不可能的。因此,設(shè)計(jì)公有的接口要非常謹(jǐn)慎,接口一旦被公開(kāi)發(fā)行,并且被廣泛實(shí)現(xiàn),在想改變這個(gè)接口幾乎是不可能的。
  • 總之,接口通常是定義允許多個(gè)實(shí)現(xiàn)的類型的最佳途徑。這條規(guī)則有個(gè)例外,即但演變的容易性和靈活性和功能更為重要的時(shí)候。

第19條:接口只用于定義類型

當(dāng)類實(shí)現(xiàn)接口時(shí),接口就充當(dāng)可以引用這個(gè)類的實(shí)例的類型。常亮接口模式是對(duì)接口的不良使用。簡(jiǎn)而言之,接口應(yīng)該只被用來(lái)定義類型,它們不應(yīng)該被用來(lái)導(dǎo)出常亮。

第20條:類層次優(yōu)于標(biāo)簽類

  • 類標(biāo)簽過(guò)于冗長(zhǎng),容易出錯(cuò),并且效率低下。為了將標(biāo)簽類轉(zhuǎn)變?yōu)轭悓哟?,首先要為?biāo)簽類中的每個(gè)方法都要定義一個(gè)包含抽象方法的抽象類,這每個(gè)方法的行為都依賴于標(biāo)簽值。
  • 簡(jiǎn)而言之,標(biāo)簽類很少有適用的時(shí)候,當(dāng)你想要編寫(xiě)一個(gè)包含顯示標(biāo)簽域的類時(shí),應(yīng)該考慮一下,這個(gè)標(biāo)簽是否可以被取消,這個(gè)類是否可以用類層次來(lái)代替,當(dāng)你遇到一個(gè)包含標(biāo)簽域的現(xiàn)有類時(shí),就要考慮將它重構(gòu)到一個(gè)層次結(jié)構(gòu)中去。

第21條:用函數(shù)對(duì)象消失策略

函數(shù)指針的主要用途就是實(shí)現(xiàn)策略模式。為了在Java中實(shí)現(xiàn)這種模式,要聲明一個(gè)接口來(lái)表示該策略,并且為每個(gè)具體策略聲明一個(gè)實(shí)現(xiàn)了該接口的類。當(dāng)一個(gè)具體策略只被使用過(guò)一次時(shí),通常是使用匿名類來(lái)聲明和實(shí)例化這個(gè)具體策略類。當(dāng)一個(gè)具體策略是設(shè)計(jì)用來(lái)重復(fù)使用的時(shí)候,他的類通常就要被實(shí)現(xiàn)為私有的靜態(tài)成員類,并通過(guò)公有的靜態(tài)final域被導(dǎo)出,其類型為該策略接口。

第22條:優(yōu)先考慮靜態(tài)成員類

  • 嵌套類是指被定義在另一個(gè)類的內(nèi)部的類。嵌套類分為四種:靜態(tài)成員類,非靜態(tài)成員類,匿名類和局部類。
  • 如果一個(gè)嵌套類需要在單個(gè)方法之外仍然是可見(jiàn)的,或者它太長(zhǎng)了,不適合于放在方法內(nèi)部,就應(yīng)該使用成員類。如果成員類的每個(gè)實(shí)例都需要一個(gè)指向其外圍實(shí)例的引用,就要把成員類做成非靜態(tài)的;否則,就做成靜態(tài)的。架設(shè)這個(gè)嵌套類屬于一個(gè)方法的內(nèi)部,如果你只需要一個(gè)地方創(chuàng)建實(shí)例,并且已經(jīng)有一個(gè)預(yù)置的類型可以說(shuō)明這個(gè)類的特征,就要把它做成匿名類;否則,就做成局部類
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 34,900評(píng)論 18 399
  • 經(jīng)典重讀——亞馬遜鏈接 筆記鏈接 導(dǎo)圖: 筆記文本: Effective Java1 第2章 創(chuàng)建和銷毀對(duì)象1.1...
    8c3c932b5ffd閱讀 1,539評(píng)論 0 1
  • 我是人世一個(gè)流毒的誓言 我是人生一份死亡的信念 我將一個(gè)可怕的影子留給你們 我對(duì)天真和善良進(jìn)行毀滅 我為一切恐怖唱...
    湘中布衣秀才閱讀 621評(píng)論 3 5
  • 有一種干糧,是包谷面磨的“牛糞餅”馕! 有一種溫暖,叫圍著火塘回憶青春和理想! 很多上一輩的苦,我們沒(méi)機(jī)會(huì)回頭吃過(guò)...
    拈花老夏閱讀 337評(píng)論 0 3
  • 浮生七月,你我各自安好。
    阿鈺的魚(yú)閱讀 137評(píng)論 0 0

友情鏈接更多精彩內(nèi)容