Chapter 8 General Programming
Item 45: Minimize the scope of local variables
local variables應該在他們要被用的地方聲明,而不應該,比如,全部聲明在方法開頭。而且在聲明的時候,記得要初始化。有個例外,比如你的
IQueryable<T> localv
需要在接下來的if else中分别初始化成不同的值,那麼你可以在if之前先聲明(我自己瞎舉的例子)。
如果某個local variable隻是這個循環需要的,那麼用for(包括foreach)比while好。因為比如最經典的for(int i = 1;i<N;i++)如果用while寫的話,要在while塊前面先寫int i = 1,這樣就很不好了,因為這個i在while執行完畢後就不需要了,這時候如果你不小心把另一個變量寫成了i,那麼編譯器并不會報錯,而for就會報錯(因為i的生命周期已經結束了)。
BTW,如果每次循環都需要計算某個值,而這個值每次都一樣的話,可以放到for的“聲明塊”裡去,比如:
for (int i = 0, n = expensiveComputation(); i < n; i++) {
doSomething(i);
}
Item 46: Prefer for-each loops to traditional for loops
在1.5版本之前,周遊一個集合或數組的方式如下:
// No longer the preferred idiom to iterate over a collection!
for (Iterator i = c.iterator(); i.hasNext(); ) {
doSomething((Element) i.next()); // (No generics before 1.5)
}
// No longer the preferred idiom to iterate over an array!
for (int i = 0; i < a.length; i++) {
doSomething(a[i]);
}
相比foreach的寫法,上面這種老寫法可讀性較差,而且容易出錯,foreach寫法:
// The preferred idiom for iterating over collections and arrays
for (Element e : elements) {
doSomething(e);
}
特别是在嵌套多層周遊的時候,foreach的優勢更能展現,如下所示:
// not preferred
for (Iterator<Suit> i = suits.iterator(); i.hasNext(); ) {
Suit suit = i.next();
for (Iterator<Rank> j = ranks.iterator(); j.hasNext(); )
deck.add(new Card(suit, j.next()));
}
// Preferred idiom for nested iteration on collections and arrays
for (Suit suit : suits)
for (Rank rank : ranks)
deck.add(new Card(suit, rank));
隻要是實作
Iterable<E>
接口的類,都可以用foreach去周遊,如果你寫的類代表了a group of elements,那麼你也可以讓它實作這個接口。
以下幾種情況不能用foreach:
一.如果你在周遊的時候需要删除某些元素,那麼你隻顯式地用一個iterator,然後用它的remove方法。
二.如果你需要修改某些元素的值。
三.如果你要“平行地”周遊多個集合(或數組)的時候,比如:
for (int i = 0,j = 0; i < a.length && j < b.length; i++,j++){...}
Item 47: Know and use the libraries
書上舉得例子是Random.nextInt(int n),傳回一個均勻分布的0到n的随機數,如果你自己實作這個方法的話,估計肯定分布得不均勻。是以說,用标準庫的好處就在于你利用了寫這個庫的專家的知識和其他所有用過這個庫的人的經驗,而且不需要你重新發明輪子。
每一個programmer都應該熟悉java.lang和java.util,最好也能掌握java.io,其他的package可以需要的時候再看。
Item 48: Avoid float and double if exact answers are required
float和double執行的是二進制浮點運算(binary floating-point arithmetic),能夠提供較為精準的快速近似計算,主要用于科學計算和工程計算。是以他們并不提供精準的結果,是以不适用于需要精确結果的場合,尤其是貨币。比如想要讓一個float或double精确地表示0.1是不可能的,比如System.out.println(1.03 - .42)的輸出結果是0.6100000000000001。解決辦法是用BigDecimal, int,或long來代替,BigDecimal需要額外的性能開銷和影響可讀性,用int和long比較好,隻是你需要自己處理好十進制小數點。但如果數值大于18位,就得用BigDecimal。
Item 49: Prefer primitive types to boxed primitives
對boxed primitives用 == 運算符幾乎是肯定錯誤的,因為這時候比較的是identity,而不是内容。如果對一個primitive和一個boxed primitives作比較,那麼boxed primitive被自動拆箱,如果它恰好是個null,那麼就NullPointerException。是以說一定要厘清到底是primitives還是boxed primitives。有一些情況下必須用boxed primitives:作為集合元素的是時候;作為泛型參數的時候;調用反射方法的時候。
Item 50: Avoid strings where other types are more appropriate
Strings不适合代替其他的類型。當資料從檔案,網絡,或鍵盤輸入傳進來的時候,經常是以字元串的形式存在,但是很多人就不管了。正确做法是應該把這種string representation轉換成他們應該是的類型,比如如果是int,就轉換成int。
Strings不适合代替enum類型。
如果你需要一個東西,它有多個元件,那麼就老老實實寫個類,别用String偷懶,比如:
String compoundKey = className + "#" + i.next();
這行代碼的意思是,你需要一個key,由一個className和一個數字組成,然後由一個#作為分隔符隔開。這種做法非常的不好。
不要用string來grant access to some functionality,書上舉的例子是ThreadLocal,這個我看的不是很懂,但是如果你複習到這裡也不用糾結了,跳過就好,相信我。
Item 51: Beware the performance of string concatenation
這個很熟悉了不解釋了,需要的時候用StringBuilder吧。
Item 52: Refer to objects by their interfaces
在head first設計模式中也提過這點,這樣做是為了更好的靈活性。當然這個原則不是絕對的,有很多情況下沒法滿足這個原則,比如String,Random,或者有時候隻能用基類(一般都是抽象的)來作為引用類型。
Item 53: Prefer interfaces to reflection
使用反射雖然很強大,但是代價是:
一.You lose all the benefits of compile-time type checking。
二.代碼冗長且可讀性差。
三.Performance suffers。
而這個item的意思是:如果某個類在編譯時不存在,是以你不得不用反射來建立他的執行個體的時候,你可以用一個interface類型(當然,必須是這個類實作的interface,或者繼承的某個super class)來refer to它的對象,進而既得到反射的強大,也可以從一定程度上得到編譯時的好處,比如你可以先用反射建立一個對象,但用一個編譯時類型來refer to它:
Set<String> s = (Set<String>)Class.forName(...).newInstance();
一旦建立完成這個對象,這裡這個s就和其他任何
Set<String>
類型的對象沒任何差別了,你可以,比如對s進行添加元素的操作:s.add(...),而不需要用反射來調用這個方法。
Item 54: Use native methods judiciously
The Java Native Interface (JNI)允許Java應用程式調用本地方法(native methods),一般用于一些platform-specific facilities,或performance-critical parts(不建議這麼做,因為JVM已經越來越成熟了)。盡量不要用本地方法,第一它們不安全,不受JVM保護;第二會影響移植性,第三難以調試并且會造成額外的性能開銷(在進和出本地方法的時候)。
Item 55: Optimize judiciously
關于優化有這麼一條真理:it is easy to do more harm than good。性能可能會與設計構架相沖突,不要為了性能而犧牲合理的結構,并且必須在設計過程中考慮性能(如果構架成型後,除非全部重寫系統,否則很難再改進性能)。比如,如果你決定讓你的API傳回一個mutable的東西,那麼你每次都要進行defensive copy,就會影響性能。
不要去計較性能上的一些小小的得失,在試着優化後要進行性能對比測試。
Profiling tools(性能剖析工具)可以幫助你找到那些需要優化的地方。
有很多因素會影響到性能,比如JVM的不同實作,不同的CPU等等。
你的原則應該是:do not strive to write fast programs—strive to write good ones; speed will follow。
Item 56: Adhere to generally accepted naming conventions
Package names應該是小寫字母(很少包括數字),然後把你的域名反着寫一下作為開頭,其餘部分應該精簡而有含義,比如util rather than utilities,一般都應該隻包含一個單詞或縮寫。
Class,interface names, including enum and annotation type names的首字母大寫,盡量不要用縮寫,如果要用的話,最好也是每個單詞的首字母大寫,比如HttpUrl。
Method and field names的首字母小寫。
constant fields應該是全部大寫,用下劃線分開。enum constants就是。
Local variable與方法命名類似,并且可以使用縮寫。
Type parameter一般就一個字母:T代表任意type, E代表一個集合的element,K代表key ,V代表value,X代表exception. 一堆不同的泛型參數可以用T, U, V或T1, T2, T3。