- 機構級別:普通會員
- 信用等級:
資料認證
未通過身份證認證
未通過辦學許可認證
- 學校瀏覽人次:次
- 加盟時間:2017年03月10日
【西安尚學堂】Effective Java學習筆記
20.用類代替結構
C語言的結構體(struct),用類(class)來代替。
一個退化到只包含一些數據域,就大體上類似C語言的結構了。而且使用類的好處還有就是可以通過封裝帶來數據保護的好處(這不就成了JavaBean了嗎)。進而Java中有這么一條告誡“公有類不應該直接暴露數據域”。
21.用類和接口來代替函數指針
在設計模式中的策略模式也是一個典型的應用場景。C語言程序通常使用函數指針來實現策略模式。Java中沒有指針,我們可以使用類和接口來替代之。
22.檢查參數的有效性
當你編寫一個方法或構造函數時,應該考慮對于它的參數都有哪些限制。你應當寫到文檔中,并在方法的開頭進行檢查。
23.需要時使用保護性拷貝
如果一個方法或構造函數允許可變對象進/出,那么就要考慮一下使用者是否有可能改變它。如果是的話,那你必須對該對象進行保護性拷貝,使進入方法內部的對象是外部時的拷貝而不它本身(因為外部的對象有可能還會被改變)。
24.慎設計方法的原型
a.謹慎選擇方法的名字
你選用的名字應該遵循標準命名習慣,選擇易懂的、并和他人保持風格一致的名字。
b.不要過于追求提供便利的方法
方法太多會是一個很難實現(對于接口而言)、學習、使用、文檔化、測試和維護的。
c.避免長長的參數列表
d.參數類型優先使用接口而不是類
e.謹慎地使用函數對象
換句話說,就是作者不建議經常使用函數回調。頻繁的創建函數對象,并且將它們從一個方法傳遞到另一個方法,這種程序設計風格并非主流.
25.謹慎地使用重載
一個安全而保守的策略是永遠不要導出兩個具有相同參數數目的重載方法
26.返回零長度的數組而不是null
避免給調用者帶來過的的保護性檢查的麻煩。返回空list而不是null。
27.為所有導出的API元素編寫文檔注釋
為了正確地編寫API文檔,你必須在每一個被導出的類、接口、構造函數、方法和域聲明之前加上文檔注釋。每一個方法的文檔注釋應該簡潔地描述出它和使用者之間的約定。
28.了解和使用庫
應該熟悉java.lang和java.util,還有一些java.io的內容。
29.如果要求精確的答案,請避免使用float和double
Java給我們準備了BigDecimal
30.將局部變量的作用域最小化
將局部變量的作用域最小化,可以增加代碼的可讀性和可維護性,并降低出錯的可能性。
31.如果其他類型更合適,請盡量避免使用字符串
這確實一個表示文本的很好的數據類型,所有的類型都有toString方法來返回一個文本。但是,它并不適合代替其他的值類型。
32.了解字符串連接的性能
涉及到多處對字符串的修改時可以嘗試使用StringBuilder
33.通過接口引用對象
應該優先使用接口而不是類來引用對象。也就是說,當我們在考慮引入一個引用變量的時候,應該首先考慮的是,我們需要一個具有什么樣功能的變量,也就是選擇一個什么接口。之后才是在眾多的實現類中選擇一個合適的實現進行初始化。
如果沒有合適的接口存在的話,那么,用類而不是接口來引用一個對象,是完全合適的。
34.接口優于反射機制
如果使用反射機制,就意味著:
a.損失了編譯時類型檢查的好處;
b.要求執行反射的代碼非常笨拙和冗長,意味著閱讀這些代碼也很困難;
c.性能損失;
以反射的方式創建一個實例后,最好通過它的接口或超類以正常的方式訪問這些實例。
35.謹慎地使用本地方法
使用JNI是有著相當的弊端的,失去了Java的平臺無關性
36.謹慎地進行優化
a.任何優化都存在風險,有時候弄不好反而帶來其他的問題
b.并不是 性能 優先。努力編寫好的程序而不是快的程序。
c.對前人,尤其是類似于Java API這樣的成熟代碼,進行優化,是不明智的(要是能優化,人家早就做了)其實,不僅僅是優化。隨著項目的進展,尤其是到了后期和交付后,再做任何代碼的改動都是非常危險的,必須十分小心,還要之后的大量、充分地測試。不論改動的目的是 需求變更/增加 還是 Debug 或是 性能優化。
37.遵守普遍接受的命名慣例
a.具體到Java的一系列命名慣例,首先是包名。Java中的package是為了區別不同人、不同公司編寫的可能名字相同的類用的,其實這句話應該反著說,因為是Java首先這么用的。Sun推薦包名按照域名的逆序來書寫,而且全是小寫字母,每一層盡量是一個英文單詞(名詞最好)。盡量一個單詞,而且盡量不大于8個字母,所以鼓勵使用縮寫。例如,使用util而不是utilities(有人也用utils)
b.類和接口(接口其實也是類,Java中萬物皆類)的名字采用 Pascal命名法,即每個單詞的首字母均大寫,各個單詞間無連接符。對于一些英文縮寫,也推薦除首字母以外都小寫。如,HttpUrl,而不是HTTPURL。這里尤其要注意的是僅有兩個字母的縮寫,如IO,ID,IP,最好還是寫成DiskIo,UserId,TerminalIp。
c.類成員(屬性和方法)名、局部變量名要用 駝峰命名法,即除首單詞的首字母要小寫外,其他同Pascal命名法。
d.常量,用全大寫,各個單詞見用下劃線“_”相連接。
e.getter/setter方法,getter/setter其實就是普通的方法,只是一種特定用途罷了。它們用于將被封裝的私有屬性對外提供訪問的方法。通常是在屬性名的前面加上 get 和 set ,再將屬性名的首字母變大寫。這里的一個特殊地方是boolean型變量,除了之上的方法,也可以把 get 改為 is,如果屬性名本身已經是以 is 開頭了,就省掉這個 is 。這是 JavaBean 的規范,廣義上講,也可以用一個名詞或名詞短語,如:size,hashCode.
f.特殊方法和屬性:
靜態工廠方法:valueOf 和 getInstance 前者廣泛用于對值類的類型轉換,后者則出現在非值類的單例模式中
類型轉換方法:toType,如 toString,toArray
返回當前對象的一個不同的視圖:asType,如:asList,常用 Arrays.asList 來將數組轉換成List
一些省略了開頭的“is”的boolean型屬性,如 initialized 和 composite
一些常用的通用屬性,如:height,digits,size 等
一些常用的通用方法,如:flush,isEmpty 等
h.在對英文單詞的選擇上,也盡量復合大多數人的習慣。
避免使用一些蹩腳的單詞,而是使用常見的單詞,而且最好是其他人也大多使用的單詞。在對兩個單詞模棱兩可時,可以在Javadoc中搜索一下,看看哪一個被類庫使用的更多。例如,當你拿不定主意用 delete 還是 remove 時,到Javadoc中搜一下,你會發現 remove 的出現次數遠比 delete 要多得多,可能僅僅是在物理上刪除如文件或數據庫中的記錄的時候才用delete,一般對變量中內容的刪除都使用remove。
38.只針對不正常的條件才使用異常
39.對于可恢復的條件使用被檢查的異常,對于程序錯誤使用運行時異常
如果希望使用者可以恢復,繼續執行程序,應該使用checkedException;而如果是程序錯誤,遇到這個錯誤后頂多是寫個日志、報告個MessageBox,就只能停止程序,就應該使用runtimeException。
兩種異常的“特征”就是,是否在調用方法時必須俘獲它可能拋出的異常。對于checkedException是必須的,而對于runtimeException是不強求的。同樣,在方法的描述中對可能拋出的checkedException要求用throws關鍵字予以明示,而runtimeException則不需要。但,請切記,這兩點區別,只是兩者表現出來的不同特征。而絕不是以要不要寫某些代碼來決定使用哪種異常的。
40.避免不必要地使用被檢查的異常
解決方案是:
a.盡量在API內部處理掉checkedException。如輸出錯誤信息或寫日志后,結束當前程序。
b.對于那些本來就是該向外拋出異常來表明內部的某種失敗的情況下,調用者盡量在調用之前確保可以成功再調用。
c.在適當的時候,使用RuntimeException。如果API不能做的更好,也許RuntimeException會更為適合。
41.盡量使用標準的異常
代碼重用,是程序員們“千百年來”所追求的目標,同樣Exception也是代碼,也該盡量被重用。所以,應該盡量使用標準的異常,而不是輕易地使用自造的異常。
IllegalArgumentException參數的值不合適。方法的開始處的參數檢查,一旦有問題就該拋出這個
IllegalStateException對于這個方法調用而言,對象狀態不合適。同樣也是開始處檢查
NullPointerException空指針
IndexOutOfBoundsException下標越界
ConcurrentModificationException 禁止并發的情況下,被檢查到并發的修改
UnsupportedOperationException 不支持這種方法,如接口/抽象父類中已經定義,但尚未實現的方法
42.拋出的異常要合適于相應的抽象
如果一個方法拋出的異常與它所執行的任務沒有明顯關聯關系的話,會使人感到不知所措。為了避免這個問題,應該在高層的實現中俘獲底層拋出的“低級”異常,并同時用另一“高級”異常繼續向外拋。
例如,如我們在方法中,有一段循環,一個一個地取集合的一下元素。當catch到一個 NoSuchElementException 或 NullPointerException 時,說明已經到頭兒了。這時我們可以 new 一個 IndexOutOfBoundsException 并向上拋出。對于此方法的調用者來說,這個異常顯然比前兩個更有實際意義。這被稱為異常轉譯。
43.每個方法拋出的異常都要有文檔
通過@throws標簽,逐個地聲明每一個被檢查的異常,并寫清每一個異常被拋出的條件(這個是通過throws語句所了解不到的)。如果一個方法可能拋出多個異常,不要用它們共通的父類異常來表示。甚至,有人連會拋出哪些異常都不關,就干脆 throws Exception (更有甚者 throws Throwable)。這種極端的做法,可能是初學者偷懶的做法,對Java的checkedException深惡痛絕的程序員。但無論如何,不要這么做。
44.在細節消息中包含失敗-捕獲信息
為了在異常發生后捕捉失敗原因,一個異常的字符串表示應該包括所有“對該異常有貢獻”的參數和域的值。也就是說在異常所能“攜帶”的信息中,盡量多的加入對調試人員有用的信息。
一個例子是 IndexOutOfBoundsException ,在這個下標越界的異常中,提示出了,最小小標、最大下標和當前下標的值,這樣調試人員就可以很清楚的知道到底是上溢出了,還是下溢出了,溢出了多少。
45.努力使失敗保持原子性
所謂失敗的原子性,就是在一個方法失敗之后,使對象保持“它在被調用之前的狀態”。因為,尤其是CheckedException發生后,一般是希望程序可以從異常中恢復過來的。
對于可變對象,常見的方法是在對其進行處理之前,先做參數有效性的檢查,如果參數有問題,馬上就先拋出一個異常。這時候對象的狀態還未改變。
另一種方法是,將處理的過程調整一下順序,讓可能拋出異常的處理寫在前面,而會改變對象狀態的處理寫在后面。
還有一種不太常用的方法,是編寫一段恢復代碼,發生失敗時,可以使對象回滾到操作開始之前的狀態。
最后一種方法是,在對象的一份臨時拷貝上執行操作,當操作正確結束后,再把臨時拷貝中的結果復制給原來的對象。如果一旦失敗,不進行這個復制,也就保持了原對象的狀態。
總之,作為方法規范的一部分,任何一個異常都不應該改變對象調用該方法之前的狀態。
46.不要忽略異常
這樣的代碼并不十分罕見,這有可能是在代碼堆砌階段的“簡單寫法”,也可能是程序員“偷懶”的寫法,當然也有可能是連“何為Exception”都不得而知的“高人”的作品。
這里犯了兩個錯誤。首先,就是本條要強調的,它忽略了捕獲來的異常。相當于是把異常抓來,然后“殺”掉它,也就是“裝沒看見”。
另一個錯誤是,不該使用異常的超類Exception,而應該“具體異常,具體分析”。
47.對共享可變數據的同步訪問
當多個線程共享可變數據的時候,每個讀或者寫數據的線程必須獲得一把鎖。