目錄:
一、創建和銷毀對象 (1 ~ 7)
二、對於所有對象都通用的方法 (8 ~ 12)
三、類和接口 (13 ~ 22)
四、泛型 (23 ~ 29)
五、枚舉和注解 (30 ~ 37)
六、方法 (38 ~ 44)
七、通用程序設計 (45 ~ 56)
八、異常 (57 ~ 65)
九、並發 (66 ~ 73)
十、序列化 (74 ~ 78)
正文:
第一章: 創建和銷毀對象
1、考慮用靜態工廠方法代替構造器
優:
① 有名稱
② 不必在每次調用它們的時候都創建一個對象
③ 可以放回原返回類型的任何子類型的對象
④ 在創建參數化類型實例的時候,代碼更簡潔
缺:
① 類如果不含公有的或受保護的構造器,就不能被子類化
② 與其他一般的靜態方法無多大區別
2、 遇到多個構造器參數時要考慮用構造器
3、用私有構造器或者枚舉類型強化 Singleton 屬性
4、通過私有構造器強化不可實例化的能力
5、避免創建不必要的對象
6、消除過期的對象引用
7、避免使用終結方法
第二章: 對於所有對象都通用的方法
8、覆蓋 equals 時請遵守通用約定
① 類的每個實例本質上是唯一的
② 不關心類是否提供了“邏輯相等”的測試功能
③ 超類已經覆蓋了 equals, 從超類繼承過來的行為對於子類也是合適的
④ 類是私有的或包級私有的,可以確定它的 equals 方法永遠不會被調用
實現高質量 equals 方法訣竅:
① 使用 “==”操作符檢查“參數是否為這個對象的引用”
② 使用 instanceof 操作符檢查“參數是否為正確的類型”
③ 把參數轉換為正確的類型
④ 對於該類中的每個“關鍵”域,檢查參數中的域是否參與該對象中的對應的域相匹配
⑤ 當你編寫完成了 equals 方法之后,應該判斷其是否滿足了 對稱性、傳遞性、一致性
-
-
- 覆蓋 equals 時總要覆蓋 hashCode
- 不要企圖讓 equals 方法過於智能
- 不要將 equals 聲明中的 Object 對象器替換為其他的類型
-
9、覆蓋 equals 時總要覆蓋 hashCode
10、始終要覆蓋 toString
11、謹慎地覆蓋 clone
12、考慮實現 Comparable 接口
第三章:類和接口
13、使類和成員的可訪問性最小化
14、在公有類中使用訪問方法而非公有域
15、使可變性最小化
① 不要提供任何會修改對象狀態的方法
② 保證類不會被擴展
③ 使所有的域都是 final 的
④ 使所有的域都成為私有的
⑤ 確保對於任何可變組件的互斥訪問
16、復合優先於繼承
簡而言之,繼承的功能非常強大,但是也存在諸多問題,因為它違背了封裝原則。只有當子類和超類之間確實存在子類類型關系時,使用繼承才是恰當的。即便如此,如果子類和超類處在不同的包中,並且超類並不是為了繼承而設計的,那么繼承將會導致脆弱性。為了避免,可以用復合和轉發機制來代替繼承,尤其是當存在適合的接口可以實現包裝類的時候。包裝類不僅比子類更加健壯,而且功能也更加強大。
17、要么為繼承而設計,並提供文檔說明,要么就禁止繼承
18、接口優於抽象類
① 現有的類可以很容易被更新,以實現新的接口
② 接口的定義的理想選擇
③ 接口允許我們構造非層次結構的類型框架
接口通常是定義允許多個實現的類型的最佳途徑。這條規律有個例外,即當演變的容易性比靈活性和功能更為重要的時候。在這種情況下,應該使用抽象類來定義類型,但前提是必須理解並且可以接受這些局限性。如果你導出了一個重要的接口,就應該堅決考慮同時提供骨架實現類。最后,應該盡可能謹慎的設計所有的公有接口,並通過編寫多個實現類對它們進行全面的測試。
19、接口只用於定義類型
接口應該只被用來定義類型,它們不應該被用來導出常量。
20、類層次優於標簽類
標簽類很少有適用的時候。當你想要編寫一個包含顯示標簽域的類時,應該考慮一下,這個標簽是否可以被取消,這個類是否可以用類層次來代替。當你遇到一個包含標簽域的現有類時,就要考慮將它重構到一個層次結構中去。
21、用函數對象表示策略
要聲明一個接口來表示該策略,並且為每個具體策略聲明一個實現了該接口的類。當一個具體策略只被使用一次時,通常實現匿名類來聲明和實例化這個具體策略類。當一個具體策略類是設計用來重復使用的時候,它的類通常就要被實現為私有的靜態成員類,並通過公有的靜態 final 域被導出,其類型為該策略接口。
22、優先考慮靜態成員類
靜態成員類、非靜態成員類、匿名類、局部類
① 如果一個嵌套類需要在單個方法之外仍然是可見的,或者他太長了,不適合於放在內部,就應該使用成員類。
② 如果成員類的每個實例都需要一個指向其外圍實例的引用,就要把成員類做成非靜態的;否則就做成靜態的。
③ 假設這個嵌套類屬於一個方法的內部,如果你只需要在一個地方創建實例,並且已經有一個預置的類型可以說明這個類的特征,就要把它做成匿名類;否則,就做成局部類。
第四章 泛型
23、請不要在新代碼中使用原生態類型
24、消除非受檢警告
每一條警告都表示可能在運行時片拋出 ClassCastException 異常。要盡最大的努力消除這些警告。如果無法消除非受檢警告,同時可以證明引起警告的代碼是類型安全的,就可以在盡可能小的范圍中,用 @SuppressWarnings("unchecked")注釋禁止該警告。要用注釋把禁止該警告的原因記錄下來。
25、列表優先於數組
數組和泛型有着非常不同的類型規則。數組是協變且可以具體化的;泛型是不可變的且可以被擦除的。因此數組提供了運行時的類型安全,當時沒有編譯時的類型安全,反之,對於泛型也一樣。一般來說,數組和泛型不能很好地混合使用。如果你發現自己將它們混合起來使用,並且得到了編譯時錯誤或警告,應該用列表代替數組。
26、優先考慮泛型
27、優先考慮泛型方法
28、利用有限制通配符來提升API 的靈活性
29、優先考慮類型安全的異構容器
第六章 枚舉和注解
30、用 enum 代替 int 常量
31、用實例域代替序數
32、用 EnumSet 代替位域
33、用 EnumMap 代替序數索引
34、用接口模擬可伸縮的枚舉
35、注解優先於命名模式
36、堅持使用 Override 注解
37、用標記接口定義類型
第七章 方法
38、檢查參數的有效性
39、必要時進行保護性拷貝
40、謹慎設計方法簽名
① 謹慎地選擇方法的名稱
② 不要過於追求提供便利的方法
③ 避免過長的參數列表
41、慎用重載
“能夠重載方法”並不意味着就“應該重載方法”。一般情況下,對於多個具有相同參數數目的方法來說,應該盡量避免重載方法。在某些情況下,特別是涉及構造器的時候,要遵循這條建議也許是不可能的。在這種情況下,至少應該避免這樣的情形:同一組參數只需經過類型轉換就可以被傳遞給不同的重載方法。
42、慎用可變參數
43、返回零長度的數組或者集合,而不是 null
44、未所有導出的 API 元素編寫文檔注釋
第八章 通用程序設計
45、將局部變量的作用域最小化
46、for-each 循環優先於傳統的 for 循環(除了 在過濾、轉換、平行迭代下外)
47、了解和使用類庫
48、如果需要精確的答案,請避免使用 float 和 double
49、基本類型優先於裝箱基本類型
50、如果其他類型更適合,則盡量避免使用字符串
① 字符串不適合代替其他的值類型
② 字符串不適合代替枚舉類型
③ 字符串不適合代替聚集類型
④ 字符串不適合代替能力表
51、當心字符串連接的性能
52、通過接口引用對象
53、接口優先於反射機制
反射的不足:
① 喪失了編譯時類型檢查的好處
② 執行反射訪問所需要的代碼非常笨拙和冗長
③ 性能損失
54、謹慎使用本地方法
55、謹慎地進行優化
56、遵守普遍接受的命名慣例
第九章 異常
57、只針對異常的情況才使用異常
58、對於可恢復的情況使用受檢異常,對於編程錯誤使用運行時異常
59、避免不必要地使用受檢的異常
60、優先使用標准的異常
61、拋出與抽象相對應的異常
62、每個方法拋出的異常都要有文檔
63、在細節消息中包含能捕獲失敗的信息
64、努力使失敗保持原子性
65、不要忽略異常
第十章 並發
66、同步訪問共享的可變數據
67、避免過度同步
為了避免死鎖和數據破壞,千萬不要從同步區域內部調用外來方法。更為一般地講,要盡量限制同步區域內部的工作量。
68、executor 和 task 優先於線程
69、並發工具優先於 wait 和 notify
70、線程安全性的文檔化
71、使用延遲初始化
72、不要依賴於線程調度器
73、避免使用線程組
第十一章 序列化
74、謹慎地實現 Serializable 接口
使用此接口代價:
① 一旦一個雷被發布,就大大降低了“改變這個類的實現”的靈活性
② 增加了出現 Bug和安全漏洞的可能性
③ 隨着類發行新的版本,相關的測試負擔也增加了
75、考慮使用自定義的序列化形式
使用默認序列化形式缺點:
① 它使這個類的導出 API 永遠地束縛在該類的內部表示法上
② 它會消耗過多的空間
③ 它會消耗過多的時間
④ 它會引起棧溢出
76、保護性地編寫 readObject 方法
77、對於實例控制,枚舉類型優先於 readResolve
78、考慮用序列化代理代替序列化實例