第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ì)象腹鹉,有助于類總能嚴(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í)際上沒有任何區(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ì)象
一般來說,最好能重用對(duì)象而不是在每次需要的時(shí)候就創(chuàng)建一個(gè)相同功能的對(duì)象疹尾。
第6條:消除過期的對(duì)象引用
- 清空對(duì)象引用應(yīng)該是一種例外上忍,而不是一種規(guī)范行為,消除過期引用最好的方法是讓包含該引用的變量結(jié)束其生命周期纳本。
- stack類自己管理內(nèi)存窍蓝,只要是類自己管理內(nèi)存,程序猿就應(yīng)該警惕內(nèi)存泄漏問題繁成。
- 內(nèi)存泄漏的另一種常見來源是緩存吓笙。只要在緩存之外存在對(duì)某個(gè)項(xiàng)的鍵的引用,該項(xiàng)就有意義巾腕,那么就可以用weakhashmap代表緩存面睛。
- 內(nèi)存泄漏的第三個(gè)常見來源是監(jiān)聽器和其他回調(diào)。確弊鸢幔回調(diào)立即被當(dāng)做垃圾回收的最佳方法是只保存它們的弱引用叁鉴,例如,只將它們保持成WeakHashMap中的鍵佛寿。
第7條:避免使用終結(jié)方法
- 終結(jié)方法(finalizer)通常是不可預(yù)測(cè)的幌墓,也是很危險(xiǎn)的,一般情況下是不必要的冀泻。
- java語言規(guī)范不僅不保證終結(jié)方法會(huì)被及時(shí)執(zhí)行常侣,而且根本不保證它們會(huì)被執(zhí)行。
- 使用終結(jié)方法有一個(gè)非常嚴(yán)重的(severe)性能損失
- 顯示的終結(jié)方法通常與try-finally結(jié)構(gòu)結(jié)合起來使用弹渔,以確保及時(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í)體而不是值的類來說確實(shí)如此压状,例如Thread。
- 不關(guān)心類是否提供了“邏輯相等”的測(cè)試功能。
- 超類已經(jīng)覆蓋了equals种冬,從超類繼承過來的行為對(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ì)象中所用的信息沒有被修改虎忌,多次調(diào)用x.equals(y)就會(huì)一致的返回true,或者一致的返回false橱鹏。
- 非空性。意思是所有的對(duì)象都必須不等于null堪藐。
實(shí)現(xiàn)提高質(zhì)量equals方法的訣竅
- 使用==操作符檢查“參數(shù)是否為這個(gè)對(duì)象的引用”莉兰。
- 使用instanceof操作符檢查“參數(shù)是否為正確的類型”。
- 吧參數(shù)轉(zhuǎn)換成正確的類型礁竞。
- 對(duì)于該類中的每個(gè)“關(guān)鍵(significant)”域糖荒,檢查參數(shù)中的域是否與該對(duì)象中對(duì)應(yīng)的域相匹配。
- 當(dāng)編寫完成了equals方法之后模捂,應(yīng)該問自己三個(gè)問題:它是是否對(duì)稱的捶朵,傳遞的,一致的狂男?
- 覆蓋equals是總要覆蓋hashCode综看。
- 不要企圖讓equals方法過于智能。
- 不要將equals聲明中的Object對(duì)象替換為其他類型岖食。
第9條 覆蓋equals是總要覆蓋hashCode
- 相等的對(duì)象必須具有相等的散列碼(hash code)
- 不要試圖從散列碼計(jì)算中出掉一個(gè)對(duì)象的關(guān)鍵部分來提高性能红碑。
第10條 始終要覆蓋toString
- 提供好的toString實(shí)現(xiàn)可以使類用起來更加舒適。
- toString方法應(yīng)該返回對(duì)象中包含的所有值得關(guān)注的信息。
- 指定toString返回值的格式也有不足之處:如果這個(gè)類已經(jīng)被廣泛使用析珊,一旦指定格式羡鸥,就必須始終如一地堅(jiān)持這種格式。
- 無論是否決定指定格式忠寻,都應(yīng)該在文檔中明確地表明你的意圖惧浴。如果要指定格式,則應(yīng)該嚴(yán)格地去做奕剃。
- 無論是否指定格式衷旅,都為toString返回值中包含的所有信息,提供一種編程式的訪問途徑祭饭。
第11條 謹(jǐn)慎地覆蓋clone
第12條 考慮實(shí)現(xiàn)Comparable接口
第4章 類和接口
第13條:是類和成員的可訪問性最小化
- 區(qū)別設(shè)計(jì)良好的模塊和設(shè)計(jì)不好的模塊芜茵,最重要的因素在于,這個(gè)模塊對(duì)于外部的其他模塊而言倡蝙,是否隱藏其內(nèi)部數(shù)據(jù)和其他實(shí)現(xiàn)細(xì)節(jié)九串。(信息隱藏/封裝)
- 盡可能地使每個(gè)類或者成員不被外界訪問
- 私有的(private)
- 包級(jí)私有的(package-private/default)
- 受保護(hù)的(protected)
- 公有的(public)
- 如果方法覆蓋了超類中的一個(gè)方法,子類中的訪問級(jí)別就不允許低于超類的訪問級(jí)別寺鸥。接口中的所有方法都隱含著公有訪問級(jí)別猪钮。
- 實(shí)例域決不能是公有的,包含公有可變域的類并不是線程安全的胆建。
- 長(zhǎng)度非零的數(shù)組總是可變的烤低,所以類具有公有的靜態(tài)final數(shù)組域,或者返回這種域的訪問方法笆载,這幾乎總是錯(cuò)誤的扑馁。
- 總之,應(yīng)該始終盡可能地降低可訪問性凉驻。除了公有靜態(tài)final域的特殊情況之外腻要,公有類都不應(yīng)該包含公有域。并且要確保公有靜態(tài)final域所引用的對(duì)象都是不可變的涝登。
第14條:在公有類中使用訪問方法而非公有域
- 如果類可以在它所在的包的外部進(jìn)行訪問雄家,就提供訪問方法,以保留將來改變?cè)擃惖膬?nèi)部表示法的靈活性胀滚。
- 如果類是包級(jí)私有的趟济,或者是私有的嵌套類,直接暴露它的數(shù)據(jù)域并沒有本質(zhì)的錯(cuò)誤咽笼。
- 公有類永遠(yuǎn)都不應(yīng)該暴露可變域顷编,雖然還是有問題,但是讓公有類暴露不可變的域其危害比較小剑刑。但是勾效,有時(shí)候會(huì)需要用包級(jí)私有的或者私有的嵌套類來暴露域,無論這個(gè)類是可變還是不可變的。
第15條:使可變性最小化
不可變類知識(shí)其實(shí)力不能被修改的類层宫。每個(gè)實(shí)例中包含的所有信息都必須在創(chuàng)建實(shí)例的時(shí)候提供杨伙,并在對(duì)象的整個(gè)周期內(nèi)固定不變。
- 不要提供任何會(huì)修改對(duì)象狀態(tài)的方法(mutator)
- 保證類不會(huì)被擴(kuò)展
- 使所有的域都是final的
- 使所有的域都成為私有的
- 確保對(duì)于任何可變組件的互斥訪問(如果類具有指向可變的域萌腿,則必須確保該類的客戶端無法獲得指向這些對(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方法編寫對(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ì)拱撵,并提供文檔說明,要么就禁止繼承
第18條:接口優(yōu)于抽象類
- 接口和抽象類的區(qū)別:抽象類允許包含某些方法的實(shí)現(xiàn)表蝙,但接口不允許拴测。為了實(shí)現(xiàn)有抽象類定義的類型,類必須成為抽象類的一個(gè)子類府蛇,Java只允許單繼承集索,所以,抽象類作為類型定義受到了極大的限制。
- 現(xiàn)有的類可以很容易被更新务荆,以實(shí)現(xiàn)新的接口妆距。
- 接口的定義mixin(混合類型)的理想選擇。(mixin允許任選的功能可被混合到類型的主要功能中)
- 接口允許我們構(gòu)造非層次結(jié)構(gòu)的類型框架函匕。
- 通過對(duì)你導(dǎo)出的每個(gè)重要的接口都提供一個(gè)抽象的骨架實(shí)現(xiàn)(skeletal implementation)類娱据,把接口和抽象類的優(yōu)點(diǎn)都集合起來。接口的作用仍然是定義類型盅惜,但是骨架實(shí)現(xiàn)類接管了所有與接口實(shí)現(xiàn)相關(guān)的工作中剩。
- 按照慣例,骨架實(shí)現(xiàn)被稱為AbstractInterface抒寂,這里的interface是指所實(shí)現(xiàn)的接口的名字结啼。例如AbstractCollection,AbstractSet屈芜,AbstractList和AbstractMap等郊愧。
- 如果設(shè)計(jì)得當(dāng),骨架實(shí)現(xiàn)可以使程序員很容易提供他們自己的接口實(shí)現(xiàn)沸伏。
- 骨架實(shí)現(xiàn)為抽象類提供了實(shí)現(xiàn)上的幫助糕珊,但又不強(qiáng)加“抽象類被用作類型定義時(shí)”所特有的嚴(yán)格限制。
- 骨架實(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))
- 編寫骨架實(shí)現(xiàn)類相對(duì)比較簡(jiǎn)單姆另,但是有點(diǎn)單調(diào)乏味喇肋。
- 因?yàn)楣羌軐?shí)現(xiàn)類是為繼承目的實(shí)現(xiàn)的,所以應(yīng)該遵循第17條中介紹的所有關(guān)于設(shè)計(jì)和文檔的指導(dǎo)原則迹辐。
- 骨架實(shí)現(xiàn)上有個(gè)小小的不同蝶防,就是簡(jiǎn)單實(shí)現(xiàn)(simple implementation),AbstractMap的SimpleEntry就是個(gè)例子明吩。
- 使用抽象類來定義允許多個(gè)實(shí)現(xiàn)的類型间学,與使用接口相比有一個(gè)明顯的優(yōu)勢(shì):抽象類的演變比接口的演變要容易得多。
- 一般來說印荔,要想在公有接口中增加方法低葫,而不破壞實(shí)現(xiàn)這個(gè)接口的所有現(xiàn)有的類,這是不可能的仍律。因此嘿悬,設(shè)計(jì)公有的接口要非常謹(jǐn)慎,接口一旦被公開發(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)該只被用來定義類型塔鳍,它們不應(yīng)該被用來導(dǎo)出常亮。
第20條:類層次優(yōu)于標(biāo)簽類
- 類標(biāo)簽過于冗長(zhǎng)呻此,容易出錯(cuò)轮纫,并且效率低下。為了將標(biāo)簽類轉(zhuǎn)變?yōu)轭悓哟畏傧剩紫纫獮闃?biāo)簽類中的每個(gè)方法都要定義一個(gè)包含抽象方法的抽象類掌唾,這每個(gè)方法的行為都依賴于標(biāo)簽值。
- 簡(jiǎn)而言之忿磅,標(biāo)簽類很少有適用的時(shí)候糯彬,當(dāng)你想要編寫一個(gè)包含顯示標(biāo)簽域的類時(shí),應(yīng)該考慮一下葱她,這個(gè)標(biāo)簽是否可以被取消撩扒,這個(gè)類是否可以用類層次來代替,當(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è)接口來表示該策略泉手,并且為每個(gè)具體策略聲明一個(gè)實(shí)現(xiàn)了該接口的類。當(dāng)一個(gè)具體策略只被使用過一次時(shí)偶器,通常是使用匿名類來聲明和實(shí)例化這個(gè)具體策略類斩萌。當(dāng)一個(gè)具體策略是設(shè)計(jì)用來重復(fù)使用的時(shí)候,他的類通常就要被實(shí)現(xiàn)為私有的靜態(tài)成員類屏轰,并通過公有的靜態(tài)final域被導(dǎo)出颊郎,其類型為該策略接口。
第22條:優(yōu)先考慮靜態(tài)成員類
- 嵌套類是指被定義在另一個(gè)類的內(nèi)部的類亭枷。嵌套類分為四種:靜態(tài)成員類,非靜態(tài)成員類搀崭,匿名類和局部類叨粘。
- 如果一個(gè)嵌套類需要在單個(gè)方法之外仍然是可見的猾编,或者它太長(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ù)置的類型可以說明這個(gè)類的特征,就要把它做成匿名類鹏氧;否則渤涌,就做成局部類