1. 程式人生 > >【軟件構造】第七章第二節 錯誤與異常處理

【軟件構造】第七章第二節 錯誤與異常處理

throw 之間 IE 程序猿 數組越界 它的 extends 希望 nds

第七章第二節 錯誤與異常處理

本節關註:Java中錯誤和異常處理的典 型技術——把原理落實到代碼上!

Outline:

  • Java中的錯誤和異常(java.lang.throwable)
  • 異常
    • Runtime異常與其他異常(Exception)
    • Checked異常和unchecked異常
  • checked異常的處理機制
  • 自定義異常

Notes:

## Java中的錯誤和異常

【Throwable】

  • Java.lang.throwable
    • Throwable 類是 Java 語言中所有錯誤或異常的超類。
    • 繼承的類:extends Object。
    • 實現的接口:implements Serializable。
    • 直接已知子類:Error, Exception(直接已知子類:IOException、RuntimeException)。

技術分享圖片

【Error】

  • Error類描述很少發生的Java運行時系統內部的系統錯誤和資源耗盡情況(例如,VirtualMachineError,LinkageError)。
  • 對於內部錯誤:程序員通常無能為力,一旦發生,想辦法讓程序優雅的結束
  • Error的類型:
    • 用戶輸入錯誤
      • 例如:用戶要求連接到語法錯誤的URL,網絡層會投訴。
    • 設備錯誤
      • 硬件並不總是做你想做的。
      • 輸出器被關閉
    • 物理限制
      • 磁盤可以填滿
      • 可能耗盡了可用內存
  • 典型錯誤:

技術分享圖片

## 異常(Exception)

  • 異常:程序執行中的非正常事件,程序無法再按預想的流程執行。
  • 異常處理:
    • 將錯誤信息傳遞給上層調用者,並報告“案發現場”的信息。
    • return之外的第二種退出途徑:若找不到異常處理程序,整個系統完全退出

【異常按結構層次的分類】

  • 運行時異常:由程序員處理不當造成,如空指針、數組越界、類型轉換
  • 其他異常:程序員無法完全控制的外在問題所導致的,通常為IOE異常,即找不到文件路徑等

【異常按處理機制角度的分類】

  • 為什麽區分checked 和 unchecked:原因其實很簡單,編譯器將檢查你是否為所有的已檢查異常提供了異常處理機制,比如說我們使用Class.forName()來查找給定的字符串的class對象的時候,如果沒有為這個方法提供異常處理,編譯是無法通過的。
  • Checked exception:
    • 編譯器可幫助檢查你的程序是否已拋出或處理了可能的異常
    • 異常的向上拋出機制進行處理,如果子類可能產生A異常,那麽在父類中也必須throws A異常。可能導致的問題:代碼效率低,耦合度過高。
    • checked exception是需要強制catch的異常,你在調用這個方法的時候,你如果不catch這個異常,那麽編譯器就會報錯,比如說我們讀寫文件的時候會catch IOException,執行數據庫操作會有SQLException等。
    • 對checked Exception處理機制    
      • 拋出:聲明是throws,拋出時throw    
      • 捕獲(try/catch):try出現異常,忽略後面代碼直接進入catch;無異常不進入catch;若catch中沒有匹配的異常處理,程序退出;若子類重寫了父類方法,父類方法沒有拋出異常,子類應自己處理全部異常而不再傳播;子類從父類繼承的方法不能增加或更改異常
      • 處理:不能代替簡單的測試,盡量苛刻、不過分細化、將正常處理與異常處理分開、利用好層次結構、早拋出晚捕獲、避免不必要的檢查
      • 清理現場、釋放資源(finally):finally中語句不論有無異常都執行

技術分享圖片

import java.io.*;
public class className
{
  public void deposit(double amount) throws RemoteException
  {
    // Method implementation
    throw new RemoteException();
  }
  //Remainder of class definition
}
  • unchecked exception:
    • 程序猿對此不做任何事情,不得不重寫你的代碼(不需要在編譯時使用try-catch等機制處理)
    • 這類異常都是RuntimeException的子類,它們不能通過client code來試圖解決
    • 這種異常不是必須需要catch的,你是無法預料的,比如說你在調用一個 list.szie()的時候,如果這個list為null,那麽就會報NUllPointerException,而這個異常就是 RuntimeException,也就是UnChecked Exception
    • 常見的unchecked exception:JVM拋出,如空指針、數組越界、數據格式、不合法的參數、不合法的狀態、找不到類等

技術分享圖片

public class NullPointerExceptionExample {
    public static void main(String args[]){
        String str=null;
        System.out.println(str.trim());
    }
}

Exception in thread "main"  java.lang.NullPointerException

【checked和unchecked總結】

  • 當要決定是采用checked exception還是Unchecked exception的時候,問一個問題: “如果這種異常一旦拋出,client會做 怎樣的補救?”
    • 如果客戶端可以通過其他的方法恢復異常,那麽采用checked exception;
    • 如果客戶端對出現的這種異常無能為力,那麽采用unchecked exception;
    • 異常出現的時候,要做一些試圖恢復它的動作而不要僅僅的打印它的信息。
  • 盡量使用unchecked exception來處理編程錯誤:因為uncheckedexception不用使客戶端代碼顯式的處理它們,它們自己會在出現的地方掛起程序並打印出異常信息。
  • 如果client端對某種異常無能為力,可以把它轉變為一個unchecked exception,程序被掛起並返回客戶端異常信息

– Checked exception應該讓客戶端從中得到豐富的信息。

– 要想讓代碼更加易讀,傾向於用unchecked exception來處理程序中的錯誤

技術分享圖片

## checked異常的處理機制

【異常中的LSP原則】

  • 如果子類型中override了父類型中的函數,那麽子類型中方法拋出的異常不能比父類型拋出的異常類型更廣泛
  • 子類型方法可以拋出更具體的異常,也可以不拋出任何異常
  • 如果父類型的方法未拋出異常,那麽子類型的方法也不能拋出異常。
  • 其他的參考第五章第二節的LSP

【利用throws進行聲明】

  • 使用throws聲明異常:此時需要告知你的client需要處理這些異常,如果client沒有handler來處理被拋出的checked exception,程序就終止執行。
  • 程序員必須在方法的spec中明確寫清本方法會拋出的所有checked exception,以便於調用該方法的client加以處理
  • 在使用throws時,方法要在定義和spec中明確聲明所拋出的全部checked exception,沒有拋出checked異常,編譯出錯,Unchecked異常和Error可以不用處理。

【利用throw拋出一個異常】

  • 步驟:
    • 找到一個能表達錯誤的Exception類/或者構造一個新的Exception類
    • 構造Exception類的實例,將錯誤信息寫入
    • 拋出它
  • 一旦拋出異常,方法不會再將控制權返回給調用它的client,因此也無需考慮返回錯誤代碼

【try-catch語句】

  • 使用 try 和 catch 關鍵字可以捕獲異常。try/catch 代碼塊放在異常可能發生的地方。
  • try/catch代碼塊中的代碼稱為保護代碼,
  • Catch 語句包含要捕獲異常類型的聲明。當保護代碼塊中發生一個異常時,try 後面的 catch 塊就會被檢查。
  • 如果發生的異常包含在 catch 塊中,異常會被傳遞到該 catch 塊,這和傳遞一個參數到方法是一樣。

【finally語句】

  • 場景:當異常拋出時,方法中正常執行的代碼被終止;但如果異常發生前曾申請過某些資源,那麽異常發生後這些資源要被恰當的清理,所以需要用finally語句。
  • finally 關鍵字用來創建在 try 代碼塊後面執行的代碼塊。
  • 無論是否發生異常,finally 代碼塊中的代碼總會被執行。
  • 在 finally 代碼塊中,可以運行清理類型等收尾善後性質的語句。
  • finally 代碼塊出現在 catch 代碼塊最後:
  • 註意下面事項:
    • catch 不能獨立於 try 存在。
    • 在 try/catch 後面添加 finally 塊並非強制性要求的。
    • try 代碼後不能既沒 catch 塊也沒 finally 塊。
    • try, catch, finally 塊之間不能添加任何代碼。
 1 public class ExcepTest{
 2   public static void main(String args[]){
 3     int a[] = new int[2];
 4     try{
 5        System.out.println("Access element three :" + a[3]);
 6     }catch(ArrayIndexOutOfBoundsException e){
 7        System.out.println("Exception thrown  :" + e);
 8     }
 9     finally{
10        a[0] = 6;
11        System.out.println("First element value: " +a[0]);
12        System.out.println("The finally statement is executed");
13     }
14   }
15 }

## 自定義異常

  • 如果JDK提供的exception類無法充分描述你的程序發生的錯誤,可以創建自己的異常類。
    • 如果希望寫一個檢查性異常類,則需要繼承 Exception 類。
    • 如果你想寫一個運行時異常類,那麽需要繼承 RuntimeException 類。

拋出檢查型異常:

技術分享圖片

拋出unchecked exception:

技術分享圖片

【軟件構造】第七章第二節 錯誤與異常處理