標籤:spark class tar int end toc http 可變對象 結束
第38條:檢查參數的有效性
對於公有的方法,要用javadoc的@throws標籤(tag)在文檔中說明違反參數值限制時會拋出的異常。這樣的異常通常為IllegalArgumentException, IndexOutOfBoundsException或NullPointerException.
非公有的方法通常應該使用斷言(assertion)來檢查它們的參數,具體做法如下所示:
第39條:必要時進行保護性拷貝
沒有對象的協助時,雖然另個類不可能修改對象的內部狀態,但是對象很容易在無意識的情況下提供這種協助。例如,考慮下面的類,它聲稱可以表示一段不可變的時間周期:
乍一看,這個類似乎是不可變的,並且強加了約束條件:周期的起始時間(start)不能在結束時間(end)之後。然而,因為Date類本身是可變的,因此很容易違反這個約束條件:
為了保護Period執行個體的內部資訊避免受到這種攻擊,時於構造器的每個可變參數進行保護性烤貝( defensive copy)是必要的,並且使用備份對象作為Period執行個體的組件,而不使用原始的對象:
注意,保護性拷貝是在檢查參數的有效性之前進行的,並且有效性檢查是針時拷貝之後的對象,而不是針時原始的時象。雖然這樣做看起來有點不太自然,卻是必要的。
第41條:慎用重載
一個反例:
你可能期望這個程式會列印出"Set" ,緊接著是“List",以及“Unknown Collection" ,但實際上不是這樣。它是列印“Unknown Collection"三次。
classify方法被重載(overloaded)了,而要調用哪個重載(overloading)方法是在編譯時間做出決定的。對於for迴圈中的全部三次迭代,參數的編譯時間類型都是相同的:Collections<>。每次迭代的運行時類型都是不同的,但這並不影響對重載方法的選擇。因為該參數的編譯時間類型為Collection<>.
這個程式的行為有悖常理,因為對於重載方法(overloaded method ]的選擇是靜態,而對於被覆蓋的方法(overridden method)的選擇則是動態。選擇被覆蓋的方法的正確版本是在運行時進行的,選擇的依據是被呼叫者法所在對象的運行時類型。
name方法是在類Wine中被聲明的,但是在類SparklingWine和Champagne中被覆蓋。正如你所預期的那樣,這個程式列印出“wine, sparkling wine和champagne" ,儘管在迴圈的每次迭代中,執行個體的編譯時間類型都為Wine。當調用被覆蓋的方法時,對象的編譯時間類型不會影響到哪個方法將被執行; “最為具體的(most specific)”那個覆蓋版本總是會得到執行。
對classify方法的最佳修正方案是,用單個方法來替換這三個重載的classify方法,並在這個方法中做一個顯式的instanceof測試:
因為覆蓋機制是規範,而重載機制是例外,所以,覆蓋機制滿足了人們對於方法調用行為的期望。正如CollectionClassifier例子所示,重載機制很容易使這些期望落空。
到底怎樣才算胡亂使用重載機制呢?這個問題仍有爭議. 安全而保守的策略是,永遠不要匯出兩個具有相同參數數目的重載方法。如果方法使用可變參數(varargs),保守的策略是根本不要重載它.
你始終可以給方法起不同的名稱,而不使用重載機制。
對於構造器,你沒有選擇使用不同名稱的機會;一個類的多個構造器總是重載的。在許多情況下,可以選擇匯出靜態工廠,而不是構造器.
JDK一個反例
在Java 1.5發行版本之前,所有的基本類型都根本不同於所有的參考型別。但是當自動裝箱出現之後,就不再如此了,它會導致真正的麻煩。考慮下面這個程式:
如果像大多數人一樣。希望程式從集合和列表中去除非整數值(0, 1和2),並列印出[-3, -2, -1] [-3, -2, -1]。事實上,列印出[-3, -2, -1] [-2, 0, -2] .
實際發生的情況是: set.remove(i)調用選擇重載方法remove(E),這裡的E是集合(Integer)的元素類型,將i從int自動裝箱到Integer中。這是你所期待的行為,因此程式不會從集合中去除正值。另一方面,list.remove(i)調用選擇重載方法remove(int i),它從列表的指定位置上去除元素。
第42條:慎用可變參數
可變參數方法接受0個或者多個指定類型的參數。可變參數機制通過先建立一個數組,數組的大小為在調用位置所傳遞的參數數量,然後將參數值傳到數組中,最後將數組傳遞給方法。
printf和反射機制都從可變參數中極大地受益。
第43條:返回零長度的數組或者集合,而不是null
對於一個返回null而不是零長度數組或者集合的方法,幾乎每次用到該方法時都需要這種曲折的處理方式。這樣做很容易出錯,因為編寫用戶端程式的程式員可能會忘記寫這種專門的代碼來處理null傳回值。
有時候會有人認為:null傳回值比零長度數組更好,因為它避免了分配數組所需要的開銷。這種觀點是站不住腳的,原因有兩點口第一,在這個層級上擔心效能問題是不明智的,除非分析表明這個方法正是造成效能問題的真正源頭(見第55條)。第二,對於不返回任何元素的調用,每次都返回同一個零長度數組是有可能的,因為零長度數組是不可變的,而不可變對象有可能被自由地共用(見第15條).
比如可以這樣做:
同樣地,集合值的方法也可以做成在每當需要返回空集合時都返回同一個不可變的空集合。Collections.emptySet, emptyList和emptyMap方法提供的正是你所需要的,如下所示:
簡而言之,傳回型別為數組或集合的方法沒理由返回null,而不是返回一個零長度的數組或者集合。
第44條:為所有匯出的API元素編寫文檔注釋
為了正確地編寫API文檔,必須在每個被匯出的類、介面、構造器、方法和域聲明之前增加一個文檔注釋。
多行的程式碼範例前使用字元
{ @code ,然後在代碼後面加上}。
不要忘記,為了產生包含HTML元字元的文檔,比如小於符號(<)、大於符號(>)以及“與”號(&),必須採取特殊的動作。讓這些字元出現在文檔中的最佳辦法是用{@literal}標籤將它們包圍起來,這樣就限制HTML標記和嵌套的Javadoc標籤的處理。
Java 1.5發行版本中增加的三個特性在文檔注釋中需要特別小心:泛型、枚舉和註解. 當為泛型或者方法編寫文檔時,確保要在文檔中說明所有的型別參數。
當為枚舉類型編寫文檔時,要確保在文檔中說明常童,以及類型,還有任何公有的方法。注意,如果文檔注釋很簡短,可以將整個注釋放在一行上:
為註解類型編寫文檔時,要確保在文檔中說明所有成員,以及類型本身。
從Java 1.5發行版本開始,包級私人的文檔注釋就應該放在一個稱作package-info.java的檔案中,而不是放在package.html中。除了包級私人的文檔注釋之外,package-info.java也可以(但並非必需)包含包聲明和包註解。
Javadoc具有“繼承”方法注釋的能力。如果API元素沒有文檔注釋,Javadoc將會搜尋最為適用的文檔注釋,介面的文檔注釋優先於超類的文檔注釋。
也可以利用{@inheritDoc}標籤從超類型中繼承文檔注釋的部分內容。這意味著,不說別的,類還可以重用它所實現的介面的文檔注釋,而不需要拷貝這些注釋。
《Effective Java》第7章 方法