第一條:考慮用靜態工廠方法代替構造函數程序員
1、優勢:編程
(1)、靜態工廠方法的一個好處是,與構造函數不一樣,靜態工廠方法具備名字。數組
(2)、靜態工廠方法的第二個好處是,與構造函數不一樣,它們每次被調用的時候,不要求非得建立一個新的對象;安全
(3)、與構造函數不一樣,它們能夠返回一個原返回類型的子類型對象;併發
2、缺點:框架
(1)、類若是不含公有的或者受保護的構造函數,就不能被子類化;函數
(2)、它們與其餘的靜態方法沒有任何區別;性能
第二條:使用私有構造函數強化singleton屬性測試
解釋:singleton是指這樣的類,他只能實例化一次,一般用來代替本質上具備惟一性的系統組件;優化
(1)、提供公有靜態成員是一個final域:
Public class Elvis{
Public static final Elvis INSTANCE = new Elvis();
Private Elvis(){
……
}
……
}
(2)、提供一個公有的靜態方法,而不是公有的靜態final域:
Public class Elvis{
Private static final Elvis INSTANCE = new Elvis();
Private Elvis(){
...
}
Public static Elvis getInstance(){
...
}
...
}
第三條:經過私有構造函數強化不可實例化的能力
(1)、企圖經過將一個類作成抽象類來強制該類不可被實例化,這是行不通的;
(2)、只要讓這個類包含單個顯式的私有構造函數,則它就不可被實例化了;
第四條:避免建立重複的對象
第五條:消除過時的對象引用
(1)、「清空對象引用」這樣的操做應該是一種例外,而不是一種規範行爲;
第六條:避免使用終結函數(finalizer)
(1)、時間關鍵的任務不該該有終結函數來執行;
(2)、咱們不該該依賴一個終結函數來更新關鍵性的永久狀態;
(3)、顯示的終止方法一般與try-finally結構結合起來使用,以確保及時終止;
(4)、終結函數用途:
a.當一個對象的全部者忘記了調用前面段落中建議的顯示終止方法的狀況下,終結函數能夠充當「安全網」。
b.與對象的本地對等體有關。
(5)、「終結函數鏈」並不會被自動執行。
第七條:在改寫equals的時候請遵照通用約定
(1)、一個類的每一個實例本質上都是惟一的。
(2)、不關心一個類是否提供了「邏輯相等」的測試功能。
(3)、超類已經改寫了equals,從超類繼承過來的行爲對於子類也是合適的。
(4)、一個類是私有的,或者是包級私有的,而且能夠肯定他的equals方法永遠也不會被調用。
-----高質量equals方法「處方」
(1)、使用==操做符檢查「實參是否爲指向對象的一個引用」;
(2)、使用instanceof操做符檢查「實參是否爲正確的類型」;
(3)、把實參轉換到正確的類型;
(4)、對於該類中每個「關鍵(significant)」域,檢查實參中的域與當前對象中對應的域值是否匹配。
(5)、當你編寫完成了equals方法以後,應該問本身三個問題:他是不是對稱的、傳遞的、一致的?
-----告誡-----------
(1)、當你改寫equals的時候,老是要改寫hashCode;
(2)、不要企圖讓equals方法過於聰明;
(3)、不要使equals方法依賴於不可靠的資源;
(4)、不要將equals聲明中的Object對象替換爲其餘的類型;
第八條:改寫equals時老是要改寫hashCode
(1)、因沒有改寫hashCode而違反的關鍵約定是第二條:相等的對象必須具備相等的散列碼;
(2)、不要試圖從散列碼計算中排除掉一個對象的關鍵部分以提升性能。
第9條:老是要改寫toString方法
(1)、提供一個好的toString方法實現,可使一個類用起來更加愉快;
(2)、在實際應用中,toString方法應該返回對象中包含的全部使人感興趣的信息;
(3)、無論你是否決定指定格式,都應該在文檔中明確地代表你的意圖;
(4)、爲toString返回值中包含的全部信息,提供一種編程訪問途徑,這老是一個好的作法;
第10條:謹慎地改寫clone
(1)、若是你改寫了一個非final類的clone方法,則應該返回一個經過調用super.clone而獲得的對象。
(2)、實際上,對於實現了Cloneable的類,咱們老是指望它也提供了一個功能適當的公有clone方法。
(3)、實際上,clone方法是另外一個構造函數;你必須確保它不會傷害到原始的對象,而且正確地創建起被克隆對象中的約束關係。
(4)、clone結構與指向可變對象的final域的正經常使用法是不兼容的。
(5)、最好的作法是,提供某些其餘的途徑來代替對象拷貝,或者乾脆不提供這樣的能力。
(6)、另外一個實現對象拷貝的好辦法是提供一個拷貝構造函數。所謂拷貝構造函數,它也是一個構造函數,其惟一的參數的類型是包含該構造函數的類。(能夠提供一個靜態工廠方法來產生)
第11條:考慮實現Comparable接口
第12條:使類和成員的可訪問能力最小化
(1)、經驗代表,你應該儘量地使每個類或成員不被外界訪問;
第13條:支持非可變性
——爲了使一個類成爲非可變類,要遵循下面五條規則:
(1)、不要提供任何會修改對象的方法;
(2)、保證沒有可被子類改寫的方法;
(3)、使全部的域都是final的;
(4)、使全部的域都成爲私有的;
(5)、保證對於任何可變組件的互斥訪問;
——優勢
(1)、非可變對象比較簡單;
(2)、非可變對象本質上是線程安全的,它們不要求同步;
(3)、非可變對象能夠被自由的共享;
(4)、你不只能夠共享非可變對象,甚至也能夠共享它們的內部信息;
(5)、非可變對象爲其餘對象——不管是可變的仍是非可變的——提供了大量的構件;
(6)、非可變類真正惟一的缺點是,對於每個不一樣的值都要求一個單獨的對象;
(7)、堅定不要爲每一個get方法編寫一個相應的set方法,除非有很好的理由要讓你個類成爲可變類,不然就應該是非可變的。
(8)、若是一個類不能被作成非可變類,那麼你仍然應該儘量的限制他的可變性;
(9)、構造函數應該建立徹底初始化的對象,全部的約束關係應該在這時候創建起來;
第14條:複合優先於繼承
(1)、與方法調用不一樣的是,繼承打破了封裝性;
第15條:要麼專門爲繼承而設計,並給出文檔說明,要麼禁制繼承
(1)、該類的文檔必須精確地描述了改寫每個方法所帶來的影響;
(2)、一個類必須經過某種形式提供適當的鉤子,以便可以進入到它的內部工做流程中,這樣的形式能夠是精心選擇的受保護(protected)的方法;
(3)、構造函數必定不能調用可被改寫的方法;
(4)、不管是clone仍是readObject,都不能調用一個可改寫的方法,無論是直接的方式,仍是間接的方式;
(5)、若是你決定爲繼承設計類,你必須使readResolve或者writeReplace成爲受保護的方法,而不是私有方法;
(6)、爲了繼承一個類,要求對這個類有一些實質性的限制;
(7)、對於那些並不是爲了安全地進行子類化而設計和編寫文檔的類,禁制子類化;
第16條:接口因爲抽象類
(1)、已有的類能夠很容易被更新,以實現新的接口;
(2)、接口是定義mixin(混合類型)的理想選擇,一個mixin是隻這樣的類型:一個類除了實現「基本類型」以外,還能夠實現這個mixin類型,以代表它提供了某些可供選擇的行爲。
(3)、接口使得咱們能夠構造出非層次結構的類型框架;
(4)、接口使得安全地加強一個類的功能成爲可能;
(5)、你能夠把接口和抽象類的優勢結合起來,對於你指望導出的每個重要接口,都提供一個抽象的骨架實現(skeletal implementation)類。骨架實現被成爲Abstract Interface;
(6)、使用抽象類來定義容許多個實現的類型,比使用接口有一個明顯的優點:抽象類的演化比接口的演化要容易得多;
第17條:接口只是被用於定義類型
(1)、常量接口模式是對接口的不良使用;
(2)、總之,接口應該只是被用來定義類型的,他們不該該被用來導出常量;
第18條:優先考慮靜態成員類
(1)、若是你聲明的成員類不要求訪問外圍實例,那麼請記住把static修飾符方法到成員類的聲明中;
第19條:用類代替結構
第20條:用類層次來代替聯合
(1)、類層次提供了類型安全性;
(2)、代碼很是簡潔明瞭;
(3)、很容易擴展,即便是多方在獨立地工做;
(4)、它能夠反映出這些類型之間本質上的層次關係;
第21條:用類來代替enum結構
(1)、 public class Suit{
Private final String name;
Private Suit(String name){this.name = name;}
Public String toString(){ return name }
Public static final Suit CLUBS = new Suit("clubs");
Public static final Suit DIAMONDS = new Suit("diamonds");
}
(2)、
public abstract class Operation {
private final String name;
Operation(String name){
this.name = name;
}
public String toString(){
return name;
}
abstract double eval(double x,double y);
public static final Operation PLUS = new Operation("+"){
double eval(double x,double y){ return x+y;}
};
}
----------
public class TestOperation {
public static void main(String[] args) {
System.out.println(Operation.PLUS.eval(2, 3));
}
}
第22條:用類和接口來代替函數指針
第23條:檢查參數的有效性
第24條:須要是使用保護性拷貝
(1)、假設類的客戶會盡一切手段來破壞這個類的約束條件,在這樣的前提下,你必須保護性的設計程序;
(2)、對於構造函數的每一個可變參數進行保護性拷貝(defensive copy)是必要的;
Public Period(Date start,Date end){
This.start = new Date(start.getTime());
This.end = new Date(end.getTime());
If(this.start.compareTo(This.end) > 0 )
Throw new IllegalArgumentException(start + " after " + end);
}
(3)、保護性拷貝動做是檢查參數的有效性以前進行的,而且有效性檢查是針對拷貝以後的對象,而不是原始對象;
(4)、對於」參數類型能夠被不可信方子類化「的情形,請不要使用clone方法進行參數的保護性拷貝;
第25條:謹慎設計方法的原型
(1)、謹慎選擇方法的名字;
(2)、不要過於追求提供便利的方法;
(3)、避免長長的參數列表;
(4)、對於參數類型,優先使用接口而不是類。
(5)、謹慎地使用函數對象;
第26條:謹慎地使用重載
(1)、對於重載方法選擇的是靜態的,而對於被改寫的方法的選擇是動態的;
(2)、一個安全而保守的策略是,永遠不要導出兩個具備相同參數數目的重載方法;
第27條:返回零長度的數組而不是null
(1)、沒有理由從一個取數組值(array-valued)的方法中返回null,而不是返回一個零長度的數組;
第28條:爲全部導出的API元素編寫文檔註釋
(1)、爲了正確地編寫API文檔,你必須在每個被導出的類、接口、構造函數、方法和域聲明以前增長一個文檔註釋;
(2)、每個方法的文檔註釋應該簡潔地描述出他和客戶之間的約定;
第29條:將局部變量的做用域最小化
(1)、使一個局部變量的做用域最小化,最有利的技術是在第一次使用它的地方聲明;
(2)、幾乎每個局部變量的聲明都應該包含一個初始化表達式;
(3)、使方法小而其中;
第30條:瞭解和使用庫
(1)、經過使用標準庫,你能夠充分利用這些編寫標準庫的專家的知識,以及在你以前其餘人的使用經驗;
(2)、你沒必要浪費時間爲那些與你的工做關係不大的問題提供特別的解決方法;
(3)、他們的性能會不斷提升,而無需你作任何努力。
(4)、你可使本身的代碼融入主流;
(5)、在每個主要的發行版本中,都會有許多新的特性被加入到庫中,因此與這些苦保持同步是值得的;
第31條:若是要求精確的答案,請避免使用float和double
第32條:若是其餘類型更合適,則儘可能避免使用字符串
(1)、字符串並不適合代替其餘的值類型;
(2)、字符串並不適合代替枚舉類型;
(3)、字符串不適合代替彙集類型;
(4)、字符串也不適合代替能力表;
第33條:瞭解字符串鏈接的性能
(1)、爲了得到可接受的性能,請使用StringBuffer代替String;
第34條:經過接口引用對象
(1)、若是你養成了使用接口做爲類型的習慣,那麼你的程序將會更加靈活;
(2)、若是沒有合適的接口存在的話,那麼,用類而不是接口來引用一個對象,是徹底合適的;
第35條:接口優先於映像機制
——使用映像類的代價
(1)、你損失了編譯時類型檢查的好處;
(2)、要求執行映像訪問的代碼很是笨拙和冗長;
(3)、性能損失;
——————
(1)、映像功能是在設計時刻被使用的:一般,普通應用在運行時刻不該該以映像方式訪問對象;
(2)、若是隻是在不少有限的狀況下使用映像機制,那麼雖然也會付出少量代價,但你能夠得到許多好處;
第36條:謹慎地使用本地方法
第37條:謹慎的進行優化
(1)、優化更容易帶來傷害,而不是好處,特別是不成熟的優化;
(2)、努力編寫好的程序,而不是快的程序;
(3)、努力避免那些限制性能的設計決定;
(4)、考慮你的API設計決定的性能後果;
(5)、好的API設計也伴隨着好的性能,爲得到好的性能而對API進行修改,這是一個很是很差的想法;
(6)、在每次試圖作優化以前和以後,請對性能進行測量;
第38條:遵照廣泛接受的命名管理
第39條:只針對不正常的條件才使用異常
(1)、顧名思義,異常只應該被用於不正常的條件,他們永遠不該該被用於正常的控制流;
(2)、一個設計良好的API不該該強迫它的客戶爲了正常的控制流而使用異常;
第40條:對於可恢復的條件使用被檢查的異常,對於程序錯誤使用運行時異常
(1)、若是指望調用者可以恢復,那麼,對於這樣的條件應該使用被檢查異常;
(2)、你所實現的全部未被檢查的拋出結構都應該是RuntimeException的子類;
第41條:避免沒必要要地使用被檢查的異常
第42條:儘可能使用標準異常
第43條:拋出的異常要適合於相應的抽象
(1)、高層的實現應該捕獲低層的異常,同時拋出一個能夠按照高層抽象進行解釋的異常;
(2)、儘管異常轉譯比不加選擇的傳遞低層異常的作法有所改進,可是它也不能被濫用;
第44條:每一個方法拋出的異常都要有文檔
(1)、老是要單獨地聲明被檢查的異常,而且利用Javadoc的@throws 標記,準確地記錄下每一個異常被拋出的條件;
(2)、使用Javadoc的@throws 標籤記錄下一個方法可能會拋出的每個未被檢查的異常,可是不要使用throws關鍵字將未被檢查的異常包含在方法的聲明中;
(3)、若是一個類中的許多方法出於一樣的緣由而拋出同一個異常,那麼在該類的文檔註釋中對這個異常作文檔,而不是爲每一個方法單獨作文檔,這是能夠接受的;
第45條:在細節消息中包含失敗-捕獲信息
(1)、爲了捕獲失敗,一個異常的字符串表示應該包含全部「對該異常有貢獻」的參數和域的值;
第46條:努力使失敗保持原子性
(1)、通常而言,一個失敗的方法調用應該使對象保持「他在被調用以前的狀態」;
(2)、做爲方法規範的一部分,任何一個異常都不該該改變對象調用該方法以前的狀態。若是這條規則被違反,則API文檔應該清楚地致命對象將會處於什麼樣的狀態,不幸的是,大量現有的API文檔都未能作到這一點;
第47條:不要忽略異常
第48條:對共享可變數據的同步訪問
(1)、synchronized關鍵字能夠保證在同一時刻,只有一個線程在執行一條語句或一段代碼塊;
(2)、你可能據說過,爲了提升性能,在讀或寫原子數據的時候,你應該避免使用同步,這個建議是很是危險而錯誤的;
(3)、爲了在線程之間可靠地通訊,以及爲了互斥訪問,同步是須要的;
(4)、通常狀況下,雙重檢查模式並不能正確地工做;
(5)、簡而言之,不管什麼時候當多個線程共享可變數據的時候,每一個讀或者寫數據的線程必須得到一把鎖;
(6)、在某些特定的條件下,使用volatile修飾符能夠提供另外一種不一樣於普通同步機制的選擇,但這是一項高級的技術;
第49條:避免過多的同步
(1)、爲了不死鎖的危險,在一個被同步的方法或者代碼塊中,永遠不要放棄對客戶的控制;
(2)、一般,在同步區域內你應該作儘量少的工做;
(3)、爲了不死鎖和數據破壞,千萬不要從同步區域內部調用外來方法,儘可能限制同步區域內部的工做量;
第50條:永遠不要在循環的外面調用wait
(1)、必定要在同步區域內調用object.wait方法,老是使用wait;
第51條:不要依賴於線程調度器
(1)、任何依賴於線程調度器而達到正確性或性能要求的程序,頗有多是不可移植的;
(2)、線程是Java平臺上最不可移植的特徵了;
(3)、對於大多數程序員來講,Thread.yield的惟一用途是在測試期間人爲的增長一個程序的併發性;
(4)、推論:不要讓應用程序的正確性依賴於線程調度器;
第52條:線程安全性的文檔化
(1)、不能經過文檔中synchronized來確認一個線程的安全性,在一個方法的聲明中出現synchronized修飾符,這是一個實現細節,並非導出API的一部分;
(2)、一個類爲了可被多個線程安全地使用,必須在文檔中清楚地說明它所支持的線程安全性級別;
第53條:避免使用線程組
(1)、線程組基本上已通過時了;
第54條:謹慎地實現Serializable
(1)、由於實現Serializable而付出的最大代價是,一旦一個類被髮布,則「改變這個類的實現」的靈活性將大大下降;
(2)、實現Serializable的第二個代價是,他增長了錯誤(bug)和安全漏洞的可能性;
(3)、實現Serializable的第三個代價是,隨着一個類的新版本的發行,相關的測試負擔增長了;
(4)、實現Serializable接口不是一個很輕鬆就能夠作出的決定;
(5)、爲了繼承而設計的類應該不多實現Serializable,接口也應該不多會擴展它;
(6)、對於爲繼承而設計的不可序列化的類,你應該考慮提供一個無參數的構造函數;
第55條:考慮使用自定義的序列化形式
(1)、若沒有認真考慮默認序列化形式是否合適,則不要接受這種形式;
(2)、若是一個對象的物理表示等同於它的邏輯內容,則默認的序列化形式多是合適的;
(3)、若是你肯定了默認序列化形式是合適的,一般你仍然要提供一個readObject方法以保證約束關係和安全性;
(4)、當一個對象的物理表示與他的邏輯數據內容有實質性的區別時,使用默認序列化形式有4個缺點:
a.它使這個類的導出API永久地約束在該類的內部表示上;
b.他要消耗過多的空間;
c.他要消耗過多的時間;
d.它會引發棧溢出;
(5)、若是全部的實例都是transient的,那麼,從技術角度而言,省去調用defaultWriteObject和defaultReadObject也是容許的,可是不推薦這樣作;
(6)、在決定講一個域作成非transient以前,請必定要確信它的值將是該對象邏輯狀態的一部分;
(7)、無論你選擇了哪一種序列化形式,你都要爲本身編寫的每一個可序列化的類聲明一個顯示的序列版本UID;
第56條:保護性地縮寫readObject方法
(1)、當一個對象反序列化的時候,對於客戶不該該擁有的對象引用,若是哪一個域包含了這樣的對象引用,則必需要作保護性拷貝,這是很是重要的;
第57條:必要時提供一個readResolve方法
(1)、readResolve方法不只僅對於singleton對象是必要的,並且對於全部其餘的實例受控的類也是必須的;
(2)、readResolve方法的第二個用法是,就像在第56條中建議的那樣,做爲保護性的readObject方法的一種保守的替代選擇;
(3)、儘管保護性readResolve模式並無被普遍使用,可是它值得認真考慮;
(4)、readResolve方法的可訪問性(accessibility)是很是重要的;