天天看點

軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試

文章目錄

  • 6.1 健壯性與正确性
  • 6.2 錯誤與異常處理(**)
    • 3. checked異常、unchecked異常
    • 8. throws
      • 8.4 LSP原則對異常的要求
    • 9. throw
      • 9.4 throw一個異常的步驟
    • 10. catch
    • 11. finally
  • 6.3 斷言與防禦機制
    • 3. 斷言VS異常
  • 6.4 debugging 代碼調試
      • 7.2. stack trace

6.1 健壯性與正确性

健壯性:保證程式可以正常進行下去(容錯)

  1. 面向内部的接口傾向于:正确性

    面向外部的接口傾向于:健壯性

  2. error(程式員犯的錯誤)——> defect/fault/bug(缺陷,bug的根源)——> failure(失效,運作時的外部表現)
  3. 衡量
    1. 外部觀察角度

      平均失效間隔時間(MTBF):失效時間+修複時間

    2. 内部觀察角度

      殘餘缺陷率:每千行代碼中遺留的bug數量

  4. 程式設計時提高健壯性與正确性的方法:

    assertion,exception,error,defensive programming(防禦式程式設計)

6.2 錯誤與異常處理(**)

注:異常用來提高程式健壯性,斷言用來提高程式正确性

  1. error與異常

    error:外部因素導緻的

    異常:

    1. 運作時異常:程式寫錯導緻的
    2. 其他異常:client調用錯誤導緻的

      (一般異常處理的不是程式出錯,而是client調用錯誤)

  2. 出現異常時會層層向上(調用者)找是否可以處理出現的異常,直到找到。

3. checked異常、unchecked異常

軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試

1. Unchecked異常:error + Runtime異常

不需要在編譯時用try…catch等機制處理,但若執行時出現會導緻程式失敗

2. Checked異常

必須捕獲并指定錯誤處理器handler,否則編譯無法通過

3. 常見Unchecked異常

ArrayIndexOutOfBoundsException——使用時超出數組邊界

NullPointerException——使用空指針時

NumberFormatException——将字元串轉換為數值類型失敗時

ClassCastException——強制轉換對象引用失敗時

  1. 關鍵字

    try

    catch

    finally(不管正确路徑還是錯誤路徑,finally下的語句都要執行)

    throws(聲明可能會發生的異常)

    throw(抛出異常)

  2. 針對checked異常編譯器檢測的兩種方式
    1. try catch捕獲
    2. 方法名後加上對應的throws,對異常進行抛出
      軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試
  3. Unchecked異常也可以throws聲明或try/catch捕獲,但不應該這麼做
checked異常 Unchecked異常
用戶端可以通過其他的額方法恢複異常 用戶端無能為力
可預料不可預防 可預料可預防
除了右邊以外的派生 派生于Error或者RuntimeException的異常

1 盡量使用unchecked異常處理程式設計錯誤(eg:NullPointerException,IllegalArgumentException)

原因:不用用戶端代碼顯示的處理

2. 如果client對某種異常無能為力,可以把它轉變成一個unchecked異常,程式被挂起并傳回用戶端異常資訊

軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試

3. 針對每一個checked 異常,建立checked異常的子類,采用繼承的方式使用戶端可以明确失敗原因并去克服。

4. 對于RuntimeException,不需要定義子類不需要繼承,就用java提供的幾種異常即可

軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試

8. throws

注:java中任何一個程式都不會顯示的通過throws語句抛出		RuntimeException(正确)
  1. 需要在方法的**spec中明确寫清本方法抛出的*所有*checked exception**,以便于調用該方法的client加以處理。
           

下圖即在規約中明确寫出

軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試
軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試

2. 抛出Unchecked異常程式不會報錯,但這種寫法是不應該的。

3. 使用throws的兩種情況:

i. 調用的函數抛出checked exception(處理不了就往上抛)

ii. 自己目前的方法抛出checked exception

8.4 LSP原則對異常的要求

1. 如果父類型抛出異常,則相對應的子類型要麼**不抛出異常**,要麼抛出的異常比父類型抛出的**一樣/更具體**。
  錯誤舉例:父抛 FileNotFoundException,子抛 IOException(x)
  2. 如果父類型中的方法沒有抛出異常,那麼子類型中的方法**必須捕獲所有的checked exception**
           

9. throw

  1. 兩種使用方式:
    1. throw new EOFException();
    2. EOFException e = new EOFException();

      throw e;

  2. **throw和throws一般在方法中是一一對應的(但是不寫throws也不會報錯)**或者throw也可以與try/catch一一對應
    軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試
  3. 自己構造異常類輸出錯誤的現場資訊方法:

    利用Exception的構造函數

    軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試

9.4 throw一個異常的步驟

  1. 找到一個可表達錯誤的異常類/構造一個新的異常類
  2. 構造Exception類的執行個體,寫入錯誤資訊
  3. 抛出它

    【注意:關于自己構造異常類的代碼】

    1.

    軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試
    2. 注意throws和throw的對應
    軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試
    3. 用輔助方法記錄“案發現場資訊”
    軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試

10. catch

  1. 擷取異常具體資訊的兩種方法
    1. e.getMessage()
    2. e.getClass().setName()
  2. catch多個類型異常的優先級
    軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試
    離try語句越近異常類越具體
  3. rethrowing 重新抛出異常

    即在catch中抛出異常

    重抛異常會把異常抛給上一級環境中的異常處理程式。同一個 try 塊的後續 catch 子句将被忽略。此外,異常對象的所有資訊都得以保持,是以高一級環境中捕獲此異常的處理程式可以從這個異常對象中得到所有資訊。

public static void f() throws Exception {
	System.out.println("a_mark");
	throw new Exception("b_mark");
}

public static void g() throws Exception {
	try {
		f();
	} catch (Exception e) {
		System.out.println("c_mark");
		e.printStackTrace(System.out);
		throw e;
	}
}  
public static void main(String[] args) {
	try {
		g();
	} catch (Exception e) {
		System.out.println("e_mark");
		e.printStackTrace(System.out);
	}
}
           

軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試

目的:更改exception的類型,友善client端擷取錯誤資訊。(注:加上se.initCause(e)是為了保留根原因,能調用se.getCause()得到異常e)

11. finally

無論是否發生異常,finally後的部分都會被執行

  1. 執行順序問題

    1. 當程式出現異常并捕獲

    軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試

    不會經過2(1的時候就中斷了)

    2. 當程式出現異常但沒有被捕獲

    軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試

    順序是1、5——沒有6的原因是程式沒有被捕獲,一般會找調用方看是否能處理異常

    3. 異常被捕獲了,但是catch中又抛出了一個異常

    順序為1、3、5

    4. 帶資源的try(TWR)

    可以帶多個資源

    軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試
  2. Stack Trace
    1. 注意順序
    2. 列印調用棧
      1. throwable提供的printStackTrace(接收類型PrintWriter(接收類型StringWriter)))
        軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試
      2. 或把調用棧的内容放在數組裡面
        軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試

6.3 斷言與防禦機制

程式開發的時候才需要斷言!

  1. 兩種形式

    assert conditon

    assert condition : message;

    例如:assert x > 0 : " x is " + x ;

  2. 斷言的檢測内容
    1. 内部不變量
    2. 表示不變量
    3. 控制流不變量
    4. 方法的前置條件(通常選擇用異常寫)
      軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試
    5. 方法的後置條件
      軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試
      B處應該為:
      軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試

3. 斷言VS異常

1.斷言能做的,異常都能做(但是斷言可以自動關閉)

2.

private一般用斷言查
public一般用異常查
傳回值用斷言檢測
  1. 防禦式程式設計
    1. 保證對不正确輸入也要有結果
    2. 路障設定(代理模式——用來校驗的功能)

6.4 debugging 代碼調試

提高程式的核心手段不是測試/debug,而是前文的模式方法

  1. 複現bug

    保證環境一緻

  2. 診斷bug
    1. print出來

      注:軟體release出去的時候,所有用于debug的print語句都要去除或禁用

      實際操作中可以用一個方法專門用來列印錯誤資訊,release時禁用方法裡那條print語句即可

      軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試
    2. 看日志(日志可以設定級别)

      例:用Hashset列印

      軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試
  3. 圍狼算法(分治)
  4. 切片
  5. 尋找差異
    1. 分析版本送出的不同
    2. 基于差異的調試(比較不同測試用例覆寫代碼的差異)
    3. 其他差異(軟硬體環境…)
  6. 符号化執行
  7. debug工具
    1. 暴力搜尋

    7.2. stack trace

    軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試
    例:判斷Stack Trace的輸出
    軟體構造複習 Chapter 66.1 健壯性與正确性6.2 錯誤與異常處理(**)6.3 斷言與防禦機制6.4 debugging 代碼調試