effective java 筆記4

來源:互聯網
上載者:User

from:http://blog.csdn.net/ilibaba/archive/2009/05/21/4207511.aspx

NO. 26 謹慎使用重載

一個常見的問題是:

public static String classify(Set s)

{ return “set”;}

public static String classify(List l)

{return “list”;}

public static String classify(Collection c)

{return “unknown collection”;}

public static void main(String[] args)

{ Collection[] tests=new Collection[]{

    new HashSet(), new ArrayList(), new HashMap.values()};

for(int i=0;i<tests.length;i++){System.out.println(classify(test[i]));}

程式結果是print出三個unknown collection。因為classify被重載了,到底調用哪個重載函數叔編譯時間刻決定的,因為編譯時間,大家的參數類型都是Collection。

避免方法重載機制的混淆。永遠不要匯出兩個具有相同參數數目的重載方法,是一個安全而保守的策略。

至少應該保證,當傳遞同樣的參數時,所有的重載方法行為一致。

NO.27 返回零長度的數組而不是null
    如果返回null,對於每次調用到該方法的時候都需要做null判斷,否則很容易拋出null 指標異常,推薦返回一個零長度的數組,在通常情況下,這樣的做法對效能幾乎沒有影響。

NO.28 為所有匯出的API元素編寫文檔注釋
需要增加註釋的地方:類、介面、建構函式、方法和域聲明,

方法注釋的內容:

調用該方法的前提條件;
調用後的後續處理(如捕獲異常);
副作用(如方法啟動線程後帶來的安全性);
參數@param Describe;
返回@return Describe;
異常@throws  if.....;

注意:注釋中可以適用<p><code><tt>等HTML標籤,但>,<等標籤需要轉義。

NO.29 講局部變數的範圍最小化
在第一次適用局部變數的地方聲明他;(不要過早地聲明它)
要防止局部變數在“使用它的塊”之外聲明,這樣會防止局部變數被意外使用;
幾乎每一個局部變數的需要初始化,如果沒有足夠的資訊來對一個變數進行有意義的初始化,那就延遲這個聲明,直到可以初始化為止;
其他的方法,例如,把一個變數多的方法分成兩個,每次操作一個方法,減少變數之間的幹擾。

NO.30 瞭解和使用庫
    不要從頭髮明輪子,如果你要做的事情是很常見的,就去查下有沒有這樣的實作類別,如果有,則使用它,這樣會降低你實現相應功能的投入和代碼的出錯率。

NO.31 如果要求精確的答案,請避免使用 float和 double
      float和double不適合表示貨幣,在平時的使用中應該避免。

例如:System.out.println(1.00-9*.10); //會輸出0.09999999999999998

如果希望系統來處理十進位的小數點,可以使用 BigDecimal。如果不考慮小數的處理,數值範圍沒有超過9位的則可以用int來處理,如果不超過18位的,則可以用long來處理,超過18位的就必須用 BigDecimal處理。

NO.32 如果其他類型更適合,則盡量避免使用字串
    如果可以使用更加合適的資料類型,或者可以編寫更加恰當的資料類型(如 DO、 POJO、枚舉等),那麼應該避免使用字串來表示對象,若使用不當,字串比其他類型更加笨拙,缺乏靈活性。

NO.33 瞭解字串串連的效能
    為串連 N個字串而重複得地使用“ +”串連,要消耗N的平方層級的時間。為了獲得更高的效能,請使用 StringBuffer代替 String。

NO.34 通過介面引用對象
    應該優先使用介面而不是類來引用對象,如果有合適的介面存在,那麼對參數的傳回值、變數和域的聲明都應該使用介面類型,如 Vector是List介面的實現,在聲明時應該如下:

    List subscribers = new Vector();  

    //而不是

    Vector subscribers = new Vector(); 

例外的情況:

① 當沒有合適的介面存在,可以用類而不是介面來引用一個對象,如: String、 Integer;

② 當一個對象是基本類型的類,而不是介面時,應該用相關的基類引用這個對象,如: java.util.TimerTask;

③ 當一個類實現了一個介面,但它提供了介面中不存在的額外方法,如果程式依賴於這些額外的方法,那麼這樣的類應該只被用來引用它的執行個體,永遠不應該被用作參數類型。

NO.35 介面優先於映像機制(反射機制)
    反射機制是 Java一項強大的功能,給定一個class執行個體,你可以獲得constructor、method和field執行個體。對於一些特定複雜的程式設計中非常必要(如現在很流行的 spring架構),但在並非必須使用反射機制時,盡量避免使用反射,原因如下:
     ① 它在編譯時間不會進行類型檢查;
     ② 實現代碼冗長乏味,不易閱讀;
     ③ 效能與一般的方法調用相比,要低下很多;
    如果一個程式必須要與編譯時間未知的類一起工作,那麼最好是用反射執行個體化對象,而訪問對象時使用編譯時間刻已知的某個介面或者父類。

NO.36 謹慎地使用本地方法
    盡量使用 Java自身提供的方法來代替本地方法(如用 Java提供的新功能來代替以前只有 C語言能實現個的功能),這樣可以使系統變得更加安全,系統可移植性更高,也使代碼變得更加容易閱讀,如果一定要使用本地方法,請加強測試,並儘可能的少用。

NO.37 謹慎地進行最佳化
    努力寫好的程式而不是快的程式,程式要體現資訊隱藏的原則,效能問題應該是設計階段就考慮,要避免那些限制效能的設計決定,如:應該用複合模式的公有類使用繼承,則該類的效能永久的受其父類效能的影響,為了獲得好的效能而對 API進行修改並非是一個好的做法,通常,這些做法對效能並沒什麼多大的影響,如果一個系統有了清晰、簡明、結構良好的實現,請謹慎對其進行最佳化,因為 80%的效能問題存在於 20%的代碼中,找出影響效能的代碼才是問題的關鍵,可以藉助一些效能分析的工具。

 

第38條:遵守普遍接受的命名慣例
java的命名慣例分為兩大類:字面的和文法的。 字面命名慣例涉及包、類、介面、方法和域。     包的名字是階層的,用句號分隔第一部分。每一部分的長度不要超過8,由小寫字母和數字組成(數字少見用),鼓勵使用有意義的縮寫。除了java和javax外,一般以網域名稱做開頭,順序是頂級網域名稱放在最前面。     類和介面的名字應至少1至多個單詞,每個單詞的首字母大寫(駝峰試),盡量避免縮寫。

方法和域的名字與類和介面的名字遵守相同的字面慣例,只是第一個首字母要小寫常量域要全部字母都大寫,詞之間通過底線區分。

文法命名慣例比字面慣例更靈活。

  1. 類通常用一個名詞或名詞短語,介面或者與類相同,或者以"-able"或"-ible"結尾的形容詞。
  2. 執行某個動作的方法,常用一個動詞或動詞短語,
  3. 對於返回boolean類型的方法,名字常以“is"開頭後加一個名詞或形容詞或短語,
  4. 如果返回的不是boolean,則常用一個名詞/短語,或以"get"開頭的動詞短語。
  5. 如果一方法所在的類是一個Bean,則強制要求以get開頭。
  6. 如果類包含對屬性操作,常用setAttribute或getAttribute格式命名。

轉換物件類型的方法,

  1. 如果返回不同類型的獨立的對象,則稱為toType
  2. 如果返回一個視圖,則用asType,
  3. 如果返回與被調用對象同值的原語類型,稱為typeValue
  4. 靜態工廠的方法,常用valueOf或getInstance.

 

NO.39 只針對不正常的條件才使用異常

異常只應該被用於不正常的條件,它們永遠不應被用於正常的控制流程。

下面是一個用異常作遍曆結束條件的濫用異常的例子:

  1. //horrible abuse of exceptions. Don't ever do this!
  2. try{ 
  3. int i=0; 
  4. while(true)a[i++].f(); 
  5. }catch(ArrayIndexOutOfBoundsException e){ 
  6.   ... 

其錯有三:

1、建立、拋出和捕獲異常的開銷是很昂貴的。因為它的初衷是用於不正常的情形,少有jvm會它進行效能最佳化。

2、把代碼放在try-catch中會阻止jvm實現本來可能要執行的某些特定的最佳化。

3、有些現代的jvm對迴圈進行最佳化,不會出現冗餘的檢查。

完全可以使用標準的實現方式:

  1. for(int i=0;i<a.length;i++){ 
  2.     a[i].f(); 

這條原則也適用於API設計。一個設計良好的API不應該強迫它的客戶為了正常的控制流程而使用異常。如果類中有一個”狀態相關”的方法,即只有 特定的條件下可被調用的方法,則這個類也應有一個單獨的“狀態測試”方法,以為調用這個狀態相關方法前的檢查。如Collection類的next方法和 hasNext方法。

  1. for(Iterator i=collection.iterator();i.hasNext();){ 
  2.     Foo foo=(Foo)i.next(); 
  3.     ... 
  4. }  

 

第40條:對於可恢複的條件使用被檢查的異常,對於程式錯誤使用運行時異常
    java提供了三種可拋出的異常:被檢查的異常(checked Exception)、運行時異常(run-time Exception)和錯誤(error)。 如果期望調用者在調用時出現的異常能夠恢複,則應該使用被檢查的異常,通過拋出一個被檢查的異常,強迫調用者在catch中處理該異常,或者將異常傳播到外面。
對於一個方法聲明要拋出的每一個被檢查的異常,它是對API使用者的一種潛在指示:與異常相關聯的條件是調用這次個方法的一種可能結果。
兩種未被檢查的可拋出結構:運行時異常和錯誤,在行為上相同的,它們都不需要、也不應該被捕獲的拋出物。你所實現的所有未被檢查的拋出結構都應是 RuntimeException的子類。定義一個非Exception、RuntimeException或Error子類的拋出物是可行的,但從行為 意義上它等同於普通的被檢查異常(即Exception子類而非RuntimeException子類).
異常是個完全意義上的對象,在其上可以定義任意的方法。因被檢查的異常往往指示了可恢複的條件,所以可通過定義方法,使調用者可獲得一些有助於恢複的資訊。具體可參見“Java異常的分類”http://blog.csdn.net/ilibaba/archive/2009/03/07/3965359.aspx

NO.41 避免不必要地使用被檢查的異常
    與傳回碼不同,被檢查的異常強迫程式處理例外的情況,從而大大地提高了程式的可靠性。而過分地使用被檢查的異常,則增加了不可忽視的負擔。如果正 確地使用API並不能阻止這種異常條件的產生,並且一旦產生了異常,使用API的程式可以採取有用的動作,那麼這種負擔被認為是正當的。

  1. try{ 
  2.    ... 
  3. }catch(TheCheckedException e){ 
  4.    e.printStackTree(); 
  5.    System.exit(1); 

如果使用API的程式員無法做得比這更好,那麼未被檢查的異常可能更為合適。在實踐中,catch幾乎總有宣告失敗的特徵。
“把被檢查的異常變成未被檢查的異常”的一種技術是,把這個要拋出異常的方法分成兩個方法,第一個方法返回一個boolean以指明是否要拋出異常,另一個執行真正的功能,如果條件不滿足就拋異常。如下:

//Invocation with checked exception

  1. try{ 
  2.    obj.action(args); 
  3. }catch(TheCheckedException e){ 
  4. //Handle exception condition

轉換為:

  1. //Invocation with state-testing method and unchecked exception
  2. if(obj.actionPermitted(args)){ 
  3.    obj.action(args)); 
  4. }else{ 
  5. //handle exception condition

當然這種轉換並不總是合適的,例如一對象將在缺少外部同步的情況下被並發訪問,或者可被外界改變狀態,那麼這種轉換將是不合適的。

 

NO.42 盡量使用標準的異常
Java平台庫中訖今為止最常被重用的異常如下:
IllegalArgumentException 參數值不合適
IllegalStateException 對於這個方法調用而言,對象的狀態不合適(如初始化不恰當)
NullPointerException 在null被禁止的情況下參數值為null
IndexOutOfBoundsException 下標越界
ConcurrentModificationException 在禁止並發修改的情況下,對象檢測到並發修改
UnsupportedOperationException 對象不支援客戶請求的方法
其它的異常也可以使用,只要確保拋出異常的條件與文檔要求一致即可。

NO.43 拋出的異常要適合於相應的抽象
高層的實現,應該捕獲低層的異常,同時拋出一個可以按照高層抽象進行解釋的異常,這種做法叫做異常轉譯(exception translation)。即如:

  1. //exception translation!
  2. try{ 
  3. //use lowlevel abstraction to do our bidding
  4.   ... 
  5. }catch(LowerLevelException e){ 
  6. throw new HigherLevelException(...); 

低層的異常被高層的異常儲存起來,且高層的異常提供一個公有的存取方法來獲得低層的異常,這種做叫做異常連結(exception chaining)。

  1. //Exception chaining.
  2. try{ 
  3. //use lower-level abstraction to do our bindding
  4.   ... 
  5. }catch(LowerLevelException e){ 
  6. throw new HigherLevelException(e); 

異常鏈的實現非常簡單,在1.4及以後版本中,可以通過Throwable來獲得支援。

  1. //Exception chaining in release 1.4 or later
  2. HigherLevelException(Throwable t){ 
  3. super(t); 

處理來自低層的異常,
最好的做法是,在調用低層方法之前通過一些檢查等手段來確保它們會成功執行;
其次的做法是,讓高層處理這些異常,從而將高層方法的調用者與低層的問題隔離開;
一般的做法是使用異常轉譯;
如果低層方法的異常對高層也是合適的,則將其從低層傳到高層。

NO.44 每個方法拋出的異常都要有文檔
      總是要單獨地聲明被檢查的異常,並且利用javadoc的@throws標記,準確地記錄下每個異常被拋出的條件。
      使用javadoc的@throws標籤積累下一個方法可能會拋出的每個未被檢查的異常,但是不要使用throws關鍵字將未被檢查的異常包含在方法的聲明中。
      如果一個類中的許多方法出於同樣的原因而拋出同一個異常,那麼在該類的文檔注釋中對這個異常做文檔,而不是為每個方法單獨寫一個文檔。

NO.45 在細節訊息中包含失敗 - 捕獲資訊
為了捕獲失敗,異常資訊應該儘可能多的包含有意義的參數和域的值,如:IndexOutOfBoundsException應該包括上界、下界以及實際的下標。

NO.46 努力使失敗保持原子性
      一個失敗的方法調用應該使對象保持“它在被調用之前的狀態”,方法如下:
① 使用非可變對象;
② 在可變對象上的操作,應在執行前檢查參數的有效性;
③ 寫一段恢複代碼,在執行失敗的情況下,復原到操作開始之前的狀態(不常用);
④ 在對象的一份臨時拷貝上執行操作,當完成之後,再把臨時拷貝中的結果複製給原來的對象,如Collections.sort在執行排序之前,先把它的輸入轉儲到一個數組中,這樣既能降低內迴圈的開銷,又能保證在排序失敗的時候,輸入列表將保持原樣。

NO.47 不要忽略異常
      除非有特殊的需求(如動畫中的多幀映像播放),否則不要吃掉異常,至少在catch塊中包含一條說明,用來解釋為什麼忽略這個異常。簡單地將一個未被檢查的異常傳播到外界至少會使程式迅速地失敗,從而保留了有助於調試該失敗條件資訊,比異常被忽略後的一個不可預測的時刻程式失敗這種情況要強。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.