第一條 考慮使用靜態(tài)工廠方法代替構(gòu)造器
優(yōu)勢(shì)
- 有名稱
- 不必每次調(diào)用的時(shí)候都創(chuàng)建一個(gè)對(duì)象
- 返回原返回類型的所有子類型的對(duì)象
- 在創(chuàng)建參數(shù)實(shí)例化的時(shí)候魏铅,他們使代碼變得更簡潔
缺點(diǎn)
- 類如果不含有共有的或者受保護(hù)的類構(gòu)造器嗤堰,就不能被子類化
- 他們與其他的靜態(tài)方法沒什么區(qū)別
第二條 遇到多個(gè)構(gòu)造器時(shí)要考慮用構(gòu)建器
構(gòu)造器參數(shù)多,個(gè)數(shù)多,則優(yōu)先使用 Builder 模式,構(gòu)建器比JavaBeans更加安全
public interface Builder<T> {
public T build();
}
第三條 用私有構(gòu)造器火鶴枚舉類型強(qiáng)化singleton屬性
單元素的枚舉類型已經(jīng)成為實(shí)現(xiàn)Singleton的最佳方法
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() {}
}
第四條 通過私有構(gòu)造器強(qiáng)化不可實(shí)例化的能力
超類也不能被實(shí)例化
public class UtilityClass {
private UtilityClass() {
throw new AssertionError();
}
}
第五條 避免創(chuàng)建不必要的對(duì)象
- 避免重復(fù)創(chuàng)建相同的對(duì)象
- 優(yōu)先使用基本類型而不是裝箱基本類型
- 不要盲目覺得創(chuàng)建對(duì)象代價(jià)十分昂貴,使用對(duì)象池的特例:數(shù)據(jù)庫連接池灿里,否則沒必要?jiǎng)?chuàng)建對(duì)象池,會(huì)影響GC
第六條 消除過期的對(duì)象引用
- 常見的場景在與ArrayList程腹,刪除對(duì)象后需要把該索引位置置空匣吊。
- 監(jiān)聽器,其他回調(diào)
可使用 WeakHashMap 將鍵保持為弱引用
第七條 避免使用終結(jié)方法
終結(jié)方法通常是不可預(yù)測的寸潦,也是很危險(xiǎn)的嗎色鸳,一般情況下是不必要的。使用終結(jié)方法是很危險(xiǎn)的见转。
終結(jié)方法線程的優(yōu)先級(jí)比應(yīng)用程序線程的優(yōu)先級(jí)低很多
// 不保證 finalize 執(zhí)行
System.gc System.runFinaliaation
// 保證執(zhí)行
System.runFinalizersOnExit Runtime.runFinalizersOnExit
第八條 覆蓋 equals 時(shí)請(qǐng)遵守通用約定
- 類是私有的或是包級(jí)私有的命雀,可以確定它的 equals 方法永遠(yuǎn)不會(huì)被調(diào)用。
防止方法被調(diào)用的方法@Ovveride public boolean equals(Object o) { throw new AssertionError(); }
equals 方法實(shí)現(xiàn)了等價(jià)關(guān)系
- 自反性
- 對(duì)稱性
- 傳遞性
- 一致性
一般做法
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceOf MyClass)) {
return false;
}
MyClass class = (MyClass) o;
// 比較各個(gè)域值
......
}
第九條 覆蓋 equals 時(shí)總要覆蓋 hashCode
Object 規(guī)范(JavaSE6)
- 同一個(gè)對(duì)象多次調(diào)用必須一致斩箫,同一個(gè)程序多次執(zhí)行可以不一致
- equals 相等吏砂,hashCode 必然要相等
- hashCode 相同,equals 不一定相同
一個(gè)好的散列函數(shù)通常傾向于“為不同的對(duì)象產(chǎn)生不相等的散列碼”
如何計(jì)算:
- 把某個(gè)非零常熟值乘客,比如說17狐血,保存到一個(gè)名為 result 的 int 類型的變量中。
- 對(duì)于對(duì)象中每個(gè)關(guān)鍵域 f (指 equals 方法中涉及到的每個(gè)域)易核,完成以下步驟:
a. 為該域計(jì)算 int 類型的散列碼 c :- 如果該域是 boolean 類型氛雪,則計(jì)算 (f ? 1 : 0)
- 如果該域是 byte ,char耸成,short,int浴鸿,則計(jì)算 (int)f
- 如果該域是 long井氢,則計(jì)算(int)(f ^ (f >> 32))
- 如果該域是 float,則計(jì)算 Float.floatToIntBits(f);
- 如果該域是 double岳链,則計(jì)算 Double.doubleToLongBits(f), 然后按照步驟 a.3花竞,計(jì)算 long 的散列值
- 如果該域是一個(gè)對(duì)象引用,并且該類的 equals 方法通過遞歸的調(diào)用 equals 的方式來比較這個(gè)域,則同樣為這個(gè)域遞歸的調(diào)用 hashCode约急。如果這個(gè)域?yàn)?null零远, 則為 0
- 如果該域是一個(gè)數(shù)組,則需要把每一個(gè)元素當(dāng)作單獨(dú)的域來處理厌蔽。
b. 按照下面的公式牵辣,把步驟2.a 中計(jì)算得到的散列碼 c 合并到 result 中:
result = 31 * result + c;
- 返回 result
- check
第十條 始終要覆蓋 toString
建議所有的子類都應(yīng)該覆蓋這個(gè)方法
第十一條 謹(jǐn)慎的覆蓋 clone
創(chuàng)建和返回該對(duì)象的一個(gè)拷貝奴饮,不必調(diào)用構(gòu)造器創(chuàng)建對(duì)象
JavaSE6 的約定內(nèi)容:
- x.clone() != x 為 true
- x.clone().getClass() == x.getClass() 為 true
- x.clone().equals(x) 為 true
一般寫法纬向,通過遞歸 super.clone() 創(chuàng)建對(duì)象,再將域逐個(gè)復(fù)制即可
第十二條 考慮實(shí)現(xiàn) Comparable 接口
實(shí)現(xiàn)了 Comparable 接口戴卜,就表明它的實(shí)例具有內(nèi)在的排序關(guān)系逾条。
當(dāng)然,也可以使用外在排序 Comparator
第十三條 使類和成員的可訪問性最小化
設(shè)計(jì)良好的模塊會(huì)隱藏所有的實(shí)現(xiàn)細(xì)節(jié)投剥,把它的API與它的實(shí)現(xiàn)清晰的隔離開來师脂。
- 盡可能地使每個(gè)類或成員不被外界訪問
- 有四種訪問性:
- 私有的(private)
- 包級(jí)私有的(default)
- 受保護(hù)的(protected)
- 共有的(public)
實(shí)例域不能是共有的
第十四條 在共有類中使用訪問方法而非共有域
有時(shí)候,可能會(huì)編寫一些退化類江锨,沒有什么作用吃警,只是用來集中實(shí)例域
Java平臺(tái)類庫中有一些違反了,但應(yīng)該警惕
共有類永遠(yuǎn)不應(yīng)該暴露可變的域
第十五條 使可變性最小化
不可變類只是其實(shí)例不能被修改的類泳桦。每個(gè)實(shí)例中包含的所有信息都必須在創(chuàng)建該實(shí)例的時(shí)候就提供汤徽,并在對(duì)象的整個(gè)生命周期內(nèi)固定不變,Java 平臺(tái)類庫中包含了許多不可變類灸撰,比如 String谒府,基本類型包裝類,BigDecimal 和 BigInteger浮毯。存在不可變的類有很多理由:不可變的類比可變的類更加容易設(shè)計(jì)完疫,實(shí)現(xiàn)和使用。
原則
- 不要提供任何會(huì)修改對(duì)象狀態(tài)的方法
- 保證類不會(huì)被擴(kuò)展
- 使所有的域都是 final 的
- 使所有的域都是私有的
- 確保對(duì)于任何可變組件的互斥訪問
第十六條 復(fù)合優(yōu)先于繼承
繼承雖然是實(shí)現(xiàn)代碼重用的有力手段债蓝,但它會(huì)打破封裝性壳鹤。而復(fù)合可以解決這個(gè)問題,只有當(dāng)子類是超類的子類型時(shí)饰迹,才適用繼承(is-a關(guān)系)芳誓。Java 平臺(tái)類庫中 Stack和Vector,Properties和Hashtable 違反了這個(gè)原則
第十七條 要么為繼承而設(shè)計(jì)啊鸭,并提供文檔說明锹淌,要么就禁止繼承
對(duì)于專門為了繼承而設(shè)計(jì)的類需要具有良好文檔說明,該文檔必須精確的描述覆蓋每個(gè)方法所帶來的影響
- 構(gòu)造器絕不能調(diào)用可被覆蓋的方法
第十八條 接口優(yōu)于抽象類
Java 程序設(shè)計(jì)語言提供了兩種機(jī)制赠制,可以用來定義允許多個(gè)實(shí)現(xiàn)的類型:接口和抽象
- 現(xiàn)有的類很容易被更新赂摆,以實(shí)現(xiàn)新的借口
- 接口是定義mixin(混合類型)的理想選擇
- 接口允許我們構(gòu)造非層次結(jié)構(gòu)的類型框架
第十九條 接口只用于定義類型
接口應(yīng)該只被用來定義類型,不應(yīng)該被用來導(dǎo)出常量
導(dǎo)出常量的做法:
public class PhysicalConstants {
// 私有
private PhysicalConstants() {}
public static final double AVOGADROS_NUMBER = 6.02214199e23;
}
第二十條 類層次優(yōu)先于標(biāo)簽類
標(biāo)簽類使得類變得復(fù)雜難懂,考慮使用類層次替換
域是不能做成 final 的
第二十一條 用函數(shù)對(duì)象表示策略
簡而言之烟号,函數(shù)指針的主要用途就是實(shí)現(xiàn)策略模式绊谭。為了在 Java 中實(shí)現(xiàn)這種模式,要聲明一個(gè)接口來表示該策略汪拥,并且為每個(gè)具體的策略聲明實(shí)現(xiàn)一個(gè)該接口的類达传。當(dāng)一個(gè)具體策略只被使用一次的時(shí)候,通常使用匿名類的方式和實(shí)例化這個(gè)具體策略類喷楣。當(dāng)一個(gè)具體策略是設(shè)計(jì)用來重復(fù)使用的時(shí)候趟大,他的類通常就要實(shí)現(xiàn)為私有的靜態(tài)成員類,并通過共有的靜態(tài)final域被導(dǎo)出铣焊,其類型為該策略接口逊朽。
第二十二條 優(yōu)先考慮靜態(tài)成員類
嵌套類被定義為在另外一個(gè)類的內(nèi)部的類,有四種:靜態(tài)成員類曲伊,非靜態(tài)成員類叽讳,匿名類,局部類坟募,后三者為內(nèi)部類岛蚤;
第二十三條 請(qǐng)不要在新代碼中使用原生態(tài)類型
例如,List 原生態(tài)類型不能再編譯器發(fā)現(xiàn)類型轉(zhuǎn)化錯(cuò)誤懈糯,而使用范型則可以涤妒,更加安全
第二十四條 消除非受檢警告
第二十五條 列表優(yōu)先于數(shù)組
第二十六條 優(yōu)先考慮范型
不能創(chuàng)建范型數(shù)組
第二十七條 優(yōu)先考慮范型方法
public static <T entends Comparable<T>> T max(List<T> list) {
Iterator<T> i = list.iterator();
T result = i.next();
while (i.hasNext()) {
T t = i.next();
if (t.compareTo(result) > 0) {
result = t'
}
}
}
第二十八條 利用有限通配符來提升API的靈活性
第二十九條 優(yōu)先考慮類型安全的異構(gòu)容器
第三十條 用 enum 代替 int
第三十一條 用實(shí)例域代替序數(shù)
第三十二條 用 EnumSet 代替位域
第三十三條 用EnumMap 代替序數(shù)索引
第三十四條 用接口模擬可伸縮的枚舉
雖然無法編寫可擴(kuò)展的枚舉類型,卻可以通過編寫接口以及實(shí)現(xiàn)該接口的基礎(chǔ)枚舉類型赚哗,對(duì)它進(jìn)行模擬她紫。
public interface Operation {
double apply(double x, double y);
}
public enum BasicOperation implements Operation {
PLUS("+") {
public double apply(double x, double y) { return x + y; }
}
private final String symbol;
BasicOperation(String symbol) {
this.symbol = symbol;
}
}
第三十五條 注解優(yōu)先于命名模式
命名模式指的是:給一些類或者方法使用有特定約束的名字
第三十六條 堅(jiān)持使用 Override 注解
堅(jiān)持使用 Override 注解能夠避免一些錯(cuò)誤
第三十七條 用標(biāo)記接口定義類型
標(biāo)記接口是沒有包含任何方法的接口,例如 Serializable 接口屿储,不包含任何方法
第三十八條 檢查參數(shù)的有效性
對(duì)于共有的可以使用 throws 說明方法會(huì)產(chǎn)生什么異常
對(duì)于私有的方法可以使用 assert 斷言
簡而言之贿讹,每當(dāng)編寫方法或者構(gòu)造器的時(shí)候,應(yīng)該考慮它的參數(shù)有哪些限制够掠。應(yīng)該把這些限制寫到文檔中民褂,并且在這個(gè)方法提開頭處,通過顯式的檢查來實(shí)施這些限制疯潭。
第三十九條 必要時(shí)進(jìn)行保護(hù)性拷貝
簡而言之赊堪,如果類具有從客戶端得到或者返回客戶端的可變組件,類就必須保護(hù)性地拷貝這些組件竖哩。如果拷貝的成本受到限制雹食,并且類信任它的客戶端不會(huì)不恰當(dāng)?shù)男薷慕M件,就可以在文檔中指明客戶端的職責(zé)是不得修改受到影響的組件期丰,以此來代替保護(hù)性拷貝
第四十條 謹(jǐn)慎設(shè)計(jì)方法簽名
- 謹(jǐn)慎的選擇方法的名稱
- 不要過于追求提供便利的方法
- 避免過長的參數(shù)列表
第四十一條 慎用重載
安全而保守的策略是,永遠(yuǎn)不要導(dǎo)出兩個(gè)具有相同參數(shù)樹木的重載方法
第四十二條 慎用可變參數(shù)
第四十三條 返回零長度的數(shù)組或者集合,而不是null
返回類型為 List 或數(shù)組钝荡,長度為零是返回 Collections.empty();
第四十四條 為所有導(dǎo)出的API元素編寫文檔注釋
第四十五條 將局部變量的作用域最小化
第四十六條 for-each 循環(huán)優(yōu)先于傳統(tǒng)的for循環(huán)
第四十七條 了解和使用類庫
第四十八條 如果需要精確的答案街立,請(qǐng)避免使用 float 和 double
第四十九條 基本類型優(yōu)先于裝箱基本類型
第五十條 如果其他類型更合適,則盡量避免使用字符串
第五十一條 當(dāng)心字符串連接的性能
第五十二條 通過接口引用對(duì)象
應(yīng)該優(yōu)先使用接口而不是類來引用對(duì)象
第五十三條 接口優(yōu)先于反射機(jī)制
第五十四條 謹(jǐn)慎的使用本地方法
第五十五條 謹(jǐn)慎的使用優(yōu)化
第五十六條 遵守普遍接受的命名習(xí)慣
第五十七條 只有針對(duì)異常的情況才使用異常
第五十八條 對(duì)可恢復(fù)的情況使用受檢異常埠通,對(duì)編程錯(cuò)誤使用運(yùn)行時(shí)異常
第五十九條 避免不必要地使用受檢的異常
第六十條 優(yōu)先使用標(biāo)準(zhǔn)的異常
第六十一條 拋出與抽象相對(duì)應(yīng)的異常
第六十二條 每個(gè)方法拋出的異常都要有文檔
第六十三條 在細(xì)節(jié)消息中包含能捕獲失敗的消息
第六十四條 努力是失敗保持原子性
第六十五條 不要忽略異常
第六十六條 同步訪問共享的可變數(shù)據(jù)
第六十七條 避免過度同步
第六十八條 executor 和 task 優(yōu)先于線程
第六十九條 并發(fā)工具優(yōu)先于 wait 和 notify
第七十條 線程安全性的文檔化
第七十一條 慎用延遲初始化
第七十二條 不要依賴于線程調(diào)度器
第七十三條 避免使用線程組
第七十四條 謹(jǐn)慎地實(shí)現(xiàn) Serializble 接口
實(shí)現(xiàn) Serializable 接口而付出第最大代價(jià)是赎离,一旦一個(gè)類被發(fā)布,就大大降低了“改變這個(gè)類的實(shí)現(xiàn)” 的靈活性