20個異常處理的最佳實踐

N1ce2cu發表於2024-07-13
  1. 儘量不要 catch RuntimeException,比如 NullPointerException、IndexOutOfBoundsException 等等,應該用預檢查的方式來規避。

  2. 儘量使用 try-with-resource 來關閉資源:禁止在 try 塊中直接關閉資源,因為一旦 close() 之前發生了異常,那麼資源就無法關閉

  3. 不要捕獲 Throwable:很可能把超出程式處理能力之外的錯誤也捕獲了

  4. 不要省略異常資訊的記錄

  5. 不要記錄了異常又丟擲了異常

  6. 不要在 finally 塊中使用 return:try 塊中的 return 語句執行成功後,並不會馬上返回,而是繼續執行 finally 塊中的語句,如果 finally 塊中也存在 return 語句,那麼 try 塊中的 return 就將被覆蓋。

   private int x = 0;
   public int checkReturn() {
       try {
           return ++x;
       } finally {
           // try 塊中 x 返回的值為 1,到了 finally 塊中就返回 2 了
           return ++x;
       }
   }
  1. 丟擲具體定義的檢查性異常而不是 Exception

  2. 捕獲具體的子類而不是捕獲 Exception 類

  3. 自定義異常時不要丟失堆疊跟蹤

  4. finally 塊中不要丟擲任何異常:如果在 finally 塊中丟擲異常,可能會導致原始異常被掩蓋

  5. 不要在生產環境中使用 printStackTrace()

  • ``printStackTrace()` 方法將異常的堆疊跟蹤資訊輸出到標準錯誤流中,這可能會暴露敏感資訊,如檔案路徑、使用者名稱、密碼等。
  • printStackTrace() 方法會將堆疊跟蹤資訊輸出到標準錯誤流中,這可能會影響程式的效能和穩定性。在高併發的生產環境中,大量的異常堆疊跟蹤資訊可能會導致系統崩潰或出現意外的行為。
  • 由於生產環境中往往是多執行緒、分散式的複雜系統,printStackTrace() 方法輸出的堆疊跟蹤資訊可能並不完整或準確。
  1. 對於不打算處理的異常,直接使用 try-finally,不用 catch
  2. 記住早 throw 晚 catch 原則
  3. 只丟擲和方法相關的異常
  4. 切勿在程式碼中使用異常來進行流程控制
  5. 儘早驗證使用者輸入以在請求處理的早期捕獲異常
  6. 一個異常只能包含在一個日誌中
// 反例
log.debug("Using cache sector A");
log.debug("Using retry sector B");

// 正例
// 在多執行緒環境中,這兩行緊挨著的日誌程式碼中間可能會輸出很多其他的內容,導致問題查起來會很難受。
LOGGER.debug("Using cache sector A, using retry sector B");
  1. 將所有相關資訊儘可能地傳遞給異常
  2. 終止掉被中斷執行緒
// 反例
while (true) {
  try {
    Thread.sleep(100000);
  } catch (InterruptedException e) {} //別這樣做
  doSomethingCool();
}

// 正例
// 應該盡最大努力完成正在做的事情,並完成當前執行的執行緒,而不是忽略 InterruptedException
while (true) {
  try {
    Thread.sleep(100000);
  } catch (InterruptedException e) {
    break;
  }
}
doSomethingCool();
  1. 對於重複的 try-catch,使用模板方法

相關文章