Effictive Java

最近看了一本書,叫作《Effictive Java中文版》,是Joshua Bloch著,潘愛民翻譯的。其實下載這本書已經好久了,可是因爲本身一直忙於其餘事情,就一直沒有看。有一天忽然又翻到,因此就從頭看了一下。java

我的感受這本書和Effictive C++很是的類似,都是從實例的角度出發,爲編程者提供了不少高效,或者說規範編程的建議。剛開始看這本書的時候感受有種醍醐灌頂的感受,畢竟本身是個學生,實際項目經驗不是很豐富,但依舊感受收益匪淺。以後看下去的時候,感受就是有種解讀java設計思想的感受了,由於後面提到的好多東西,其實在缺少開發經驗的前提下並非能有很大的收穫。本人日常看書有總結的習慣,老是喜歡用幾句話來總結本身看過的內容,因此在這裏將本身寫的東西記錄一下,以供後期再次回味。程序員

第二章 建立和銷燬對象
1. 靜態工廠和構造函數,在設計的時候能夠考慮用靜態工廠方法來替代構造函數編程

2. singleton從大的方面能夠分爲餓漢式和飽漢式兩種方式
餓漢式確保了該類永遠是一個singleton,而飽漢式其實還保留了一點餘地
singleton在序列化的時候須要提供一個readResolve,不然在每次反序列化的時候,都會建立一個新的實例數組

3. 有些類並不想被實例化,而是僅僅做爲工具類的存在,好比Arrays,或者Collections等
這種類在編寫的時候能夠用私有構造函數來強化不可實例化的能力
例如:Arrays的構造函數是 private Arrays() {}瀏覽器

4. 避免建立重複的對象,若是一個對象是不可變的,並且建立時耗時或者消耗內存的,應該儘可能去複用,而不是建立安全

5. 消除過時的對象引用,當一個對象再也不被使用時,應該儘可能清楚他內部全部對象的引用多線程

6. 避免使用終結函數,終結函數(finalize)的執行時間以及是否執行是不可肯定的
在須要釋放某些資源的時候,應該儘可能使用try-finally結構來處理,而不能寄但願於finalize函數併發

第三章 對於全部對象都通用的方法
7. 在改寫equals方法的時候要注意該方法的一些特性:自反性,對稱性,傳遞性,一致性等 框架

8. 改寫equals時老是要改寫hashCode,由於Object類規定,相等的對象必須具備相等的散列碼(hash code)函數

9. 老是要改寫toString,建議全部的子類都改寫這個方法,從而獲取本身想要的信息

10. 謹慎地改寫clone, object的clone方法是protected的,是被保護的。
實現克隆接口的時候或者進行拷貝的時候,要注意深度拷貝和淺拷貝的區別
還能夠實現一個拷貝構造函數或者靜態工廠方法的變形

11. 考慮實現Comparable接口,與equals方法很是類似,但也有一些不一樣
不須要檢查實參的類型,若是類型不合適,會拋出ClassCastException異常
若是實參是null,會拋出NullPointerException異常

第四章 類和接口
12. 使類和成員的可訪問能力最小化,儘量地下降可訪問性

13. 支持非可變性:一個非可變類是一個簡單的類,它的實例不能被修改
堅定不要爲每個get方法編寫相應的set方法,除非有很好的理由要讓一個類變成可變類
即便一個類不是非可變類,那麼也應該儘可能限制它的可變性

14. 複合優先於繼承,與方法調用不一樣,繼承打破了封裝性,子類依賴於超類的功能實現,超類變了,子類就必需要變化
繼承:在一個包的內部使用繼承是很是安全的
或者專門爲了繼承而設計,而且具備良好文檔說明的類,使用繼承也是很好的
複用:不是擴展一個已有的類,而是在新的類中增長一個私有域,引用了已有類的一個實例。其實相似於一個包裝類

在java中,stack並非vector,所以應該使用複用,而不是繼承
一樣的,屬性列表也不是一個散列表,所以Properties不該該擴展Hashtable

繼承會把超類中的缺陷傳播到子類中,而複合能夠設計新的API,從而隱藏這些缺陷

15. 要麼專門爲繼承而設計,並給出文檔說明,要麼禁止繼承
爲了繼承而設計一個類,要求對這個類有一些實質性的限制,這並非輕鬆能夠決定的

防止類被子類化:1——把這個類聲明成final的
2——把全部的構造函數聲明爲私有的,或者說是包級私有的

16. 接口優於抽象類:接口使得咱們能夠構造出非層次結構的類型框架
接口使得安全地加強一個類的功能成爲可能,經過包裝類的模式
抽象類能夠用來作爲一個骨架實現類,而後用各類接口來進行具體功能的實現和擴展
抽象類的演化比接口的演化要容易得多,能夠更加方便地增長新的方法

17. 接口只是被用於定義類型的:一個類實現了一個接口,這個接口就是一個類型,經過此類型能夠引用這個類的實例
常量接口,包含了不少靜態的final域,實現該接口的類均可以使用這些常量(不建議這樣用)
接口應該只是被用來定義類型的,不該該被用來導出常量

18. 優先考慮靜態成員類,嵌套類有四種:靜態成員類,非靜態成員類,匿名類,局部類

靜態成員類:外圍類的一個成員,能夠訪問外圍類的全部成員
非靜態成員類:每個實例都隱含着與外圍類的一個外圍實例緊密關聯在一塊兒,會浪費時間和空間
局部類:在能夠聲明局部變量的地方,能夠聲明局部類。與成員類同樣,局部類有名字,可被重複使用
匿名類:沒有名字,不是外圍類的一個成員。在被使用的點上被聲明和實例化,能夠出如今任何容許代碼出現的地方,好比Thread,Runnable實例

第五章 C語言結構的替代
19. 用類代替結構,類能夠把數據封裝到一個對象中,而且只有對象的方法才能夠訪問這些數據。
公有類不該該直接暴露數據域,而應該使用get和set函數等

20. 用類層次來代替聯合(union),能夠定義一個抽象類,而後擴展其餘類來實現功能
類層次提供了類型安全性,並且代碼更加簡潔,容易擴展,反映類型之間本質上的層次關係

21. 用類來代替enum結構,java中存在一種類型安全枚舉,定義一個類來表明枚舉類型的單個元素
對於任何枚舉類型的需求都應該相對較少,由於隨着子類化技術的推廣,這些類型的主要用途已通過時了。

22. 用類和接口來代替函數指針
C語言的函數指針主要用途是實現Strategy(策略)模式。java中能夠用一個接口表示該策略,並聲明一個實現該接口的類

第六章 方法
23. 檢查參數的有效性
應該在方法體的起始處對參數進行檢查,非公有的方法一般應該使用assertions(斷言)來檢查它們的參數
編寫方法或者構造函數的時候,應該考慮參數有哪些限制

24. 須要時使用保護性拷貝
保護性拷貝動做是在檢查參數的有效性以前進行的,並且有效性檢查是針對拷貝以後的對象,而不是原始的對象

25. 謹慎設計方法的原型
1——謹慎選擇方法的名字
2——不要過於追求提供便利的方法:每個方法都應該提供其應具有的功能點。不要定義太多的方法
3——避免長長的參數列表:三個參數應該被看做實踐中的最大值,參數應該越少越好
4——對於參數類型,優先使用接口而不是類:這樣方便後來的擴展
5——謹慎地使用函數對象

26. 謹慎地使用重載
重載方法的選擇是靜態的,並不能保證重載的函數執行的是本身邏輯上想要他執行的那一個
一個安全保守的策略是,永遠不要導出兩個具備相同參數數目的重載方法

27. 返回零長度的數組而不是null
返回null會致使程序員須要額外的代碼來處理這種狀況

28. 爲全部導出的API元素編寫文檔註釋
java語言環境提供了一個被稱爲javadoc的實用工具,可根據源代碼自動產生API文檔
在文檔註釋內部能夠出現任意的HTML標籤,可是HTML元字符必需要通過轉義

第七章 通用程序設計

29. 局部變量的做用域最小化
c程序設計語言要求局部變量必須被聲明在一個代碼塊的開始處
java程序語言容許在任何能夠出現的地方聲明變量,提升代碼的可讀性

30. 瞭解和使用庫
瞭解標準庫的函數和使用方法,會讓你的代碼變得更加簡單
不要從頭髮明輪子,若是你要作的事情看起來很常見,可能庫中已經封裝了相應的函數

31. 若是要求精確的答案,請避免使用float和double
float和double類型的主要設計目標是科學計算和工程計算,對於貨幣計算很是不合適
精確計算應該更多的使用BigDecimal,int或者long等進行貨幣計算

32. 若是其餘類型更加適合,則儘可能避免使用字符串
字符串不適合代替枚舉類型,彙集類型,能力表等

33. 瞭解字符串鏈接的性能
字符串鏈接符並不適合規模比較大的情形,儘可能使用StringBuffer

34. 經過接口引用對象
例如:List subscribers = new Vector() 使用接口轉變會更加的靈活,特別是工廠模式

35. 接口優先於反射(reflection)機制
反射機制能夠經過程序來訪問已裝載的類的信息
複雜的應用程序須要使用反射機制,包括瀏覽器,對象監視器,代碼分析工具,內嵌的解釋器等

反射機制的缺點:1. 損失了編譯時類型檢查的好處,可能會調用不存在的方法
2. 要求執行映像訪問的代碼很是笨拙和冗長
3. 性能損失,耗時多是普通方法的幾十倍

36. 謹慎地使用本地方法(JNI)
使用本地方法主要有幾種好處:1. 訪問與平臺相關的設施,好比註冊表和文件鎖等
2. 訪問老式代碼庫以及老式數據等
3. 使用本地的方法,從而提升性能等
使用本地方法的缺點:1. 使用本地的語言多是不安全的
2. 因爲本地方法是平臺相關的,所以使用了本地的方法,程序也再也不是可自由移植的
3. 對於每個平臺,本地代碼須要從新編譯
4. 在進入和退出本地代碼時,須要較高的程序開銷

37. 謹慎地進行優化
在每次試圖作優化以前和以後,應該對性能進行測量

38. 遵照廣泛接受的命名慣例
字面的:主要涉及到包,類,接口,方法和域等的命名,如大小寫習慣等
語法的:類一般用一個名詞或者名詞短語命名(接口相似)
執行某個動做的方法一般用一個動詞或者動詞短語來命名

第八章 異常
39. 只針對不正常的條件才使用異常(永遠不該該被用於正常的控制流)
異常的設計初衷是用於不正常的情形。因此建立,拋出和捕獲異常的開銷都是很昂貴的

40. 對於可恢復的條件使用被檢查的異常,對於程序錯誤使用運行時異常
java中有三種可拋出結構:1.被檢查的異常(checked exception),可恢復的條件
2.運行時異常(run-time exception),程序錯誤
3.錯誤(error),每每是不可恢復的情形

41. 避免沒必要要地使用被檢查的異常

42. 儘可能使用標準的異常
重用現有的異常:1.使得你的API更加易於學習和使用
2.對於用到這些API的程序而言,它們的可讀性更好
3.異常類越少,意味着內存佔用越小,裝載類的開銷也越小

43. 拋出的異常要適合於相應的抽象
底層異常的處理:1.可用使用異常轉譯來處理
異常轉譯:高層的實現應該捕獲底層的異常,同時拋出一個能夠按照高層抽象進行解釋的異常
2.若是能夠的話,應該在處理來自底層異常的時候,確保它們會成功地執行

44. 每一個方法拋出的異常都要有文檔
使用javadoc的@throws標記,準確地記錄下每一個異常被拋出的條件
若是一個類中的許多方法因爲一樣的緣由而拋出同一個異常,那麼在該類的文檔註釋中作文檔是能夠的

45. 在細節消息中包含失敗-捕獲信息
異常類型的toString方法應該儘量多地返回有關失敗緣由的信息
一個異常的字符串表示應該包含全部「對該異常有貢獻」的參數和域的值
棧軌跡一般包含了異常被拋出的確切文件和行數

46. 努力使失敗保持原子性
一個失敗的方法調用應該使對象保持在「它被調用以前的狀態」(失敗原子性)
失敗原子性得到的辦法:1.設計一個非可變的對象,操做失敗將會阻止建立新的對象
2.對計算處理過程調整順序,使得任何可能失敗的計算部分都發生在對象狀態被修改以前
3.編寫一段恢復代碼,使對象回滾到操做開始以前的狀態
4.在對象的一份臨時拷貝上執行操做,操做完以後再複製給原來的對象

47. 不要忽略異常
空的catch塊會使異常達不到應有的目的。忽略異常會致使程序在遇到錯誤的時候繼續悄然地執行下去

第九章 線程

48. 對共享可變數據的同步訪問
在使用多個線程共享可變數據的時候,每一個讀或者寫數據的線程必須得到一把鎖


49. 避免過多的同步
過多的同步可能會致使性能下降,死鎖,甚至不肯定的行爲
一般,在同步區域內應該作儘可能少的工做。得到鎖,檢查共享數據,變換數據,釋放鎖。
爲了不死鎖和數據破壞,千萬不要在同步區域內部調用外來方法

50. 永遠不要再循環的外面調用wait
object.wait方法是使一個線程等待某個條件,必定是在同步區域中被調用的
在選擇notify仍是notifyAll的時候,老是建議使用notifyAll喚醒全部線程,讓線程去檢測是否知足條件
老是在一個while循環中調用wait,而且使用標準模式

51. 不要依賴於線程調度器
線程調度器決定哪一個線程運行以及運行時間。但不一樣的JVM可能會有區別,依賴於調度器的多線程多是不可移植的
健壯的,相應良好的,可移植的多線程調度器應該確保在任什麼時候刻只有少許的可運行線程
讓每一個線程作少許的工做,而後使用object.wait等待條件或者Thread.sleep睡眠一段時間
線程優先級是java平臺上最不可移植的特徵,所以儘可能不要使用這個方法來改善應用程序
不要試圖經過Thread.yield來修正程序,由於不一樣的JVM對於yield有不一樣的反映

52. 線程安全性的文檔化
一個類可能支持的線程安全性級別:1.非可變的,類的實例是不變的,如String,Integer,BigInteger等
2.線程安全的,類的實例是可變的,但全部方法都包含足夠的同步機制
3.有條件的線程安全,這個類的某些方法必須被順序調用
4.線程兼容的,在每一個方法調用的外圍使用外部同步
5.線程對立的,這個類不能安全地被多個線程併發使用,這樣的類或者方法在java中很是少

53. 避免使用線程組
線程組容許你把Thread的基本功能直接應用到一組線程上
線程組基本上已通過時了,不建議使用

第十章 序列化
序列化是將一個對象編碼成一個字節流,反序列化則是將字節流轉變爲對象
對象被序列化以後能夠從一個正在運行的虛擬機傳遞到另外一個虛擬機上,或者存儲到磁盤中

54. 謹慎地實現Serializable
應該設計一個高質量的序列化形式,否則後期改變這個類會很麻煩
默認的序列化會致使類中私有的和包級私有的實例域都變成導出API的一部分
一個類實現了Serializable後,隨着新版本的發行,相關的測試負擔增長了
實現Serializable是一個很嚴重的承諾(除非該類一段時間後會被拋棄)

55. 考慮使用自定義的序列化形式
理想的序列化形式應該只包含該對象所表示的邏輯數據。
若是沒有認真考慮默認序列化形式是否合適,則不要接受這種形式
即便確認了默認序列化形式合適,也須要一個readObject方法以保證約束關係和安全性
無論選用了哪一種序列化形式,都要爲每一個可序列化的類聲明一個顯式的序列版本UID
一個對象的物理表示與他的邏輯數據內容有實質差異時,默認的序列化有4個缺點:
1. 使這個類的導出API永遠束縛在該類的內部表示上
2. 消耗過多的空間,物理表示和邏輯數據都要存儲起來
3. 消耗過多的時間,序列化邏輯須要昂貴的圖遍歷
4. 有可能引發棧的溢出,默認的序列化過程會對對象圖進行一次遞歸遍歷

56. 保護性地編寫readObject方法
readObject方法至關於一個構造函數。所以也須要檢查參數的有效性以及必要時對參數進行保護性拷貝
保護性拷貝應該是在有效性檢查以前進行的

57. 必要時提供一個readResolve方法 若是一個類定了readResolve方法,在反序列化的時候,新建立的對象會調用這個方法,而這個新建立的對象再也不有用 readResolve方法不只對singleton對象是必要的,對於其餘的實例受控的類也是必須的 readResolve方法能夠替代保護性的readObject方法

相關文章
相關標籤/搜索