這一簡短系列的 第 1 部分 介紹了如何進行有效測試,它構建了 FindBugs 插件來查找一個簡單的 bug 模式(只需調用 System.gc()
即可)。Bug 模式會標識有問題的編碼實踐,它們經常位于 bug 所在的區域。當然,并不是所有出現 bug 模式的地方都一定出現 bug,但這并不能抹殺 bug 模式檢測器的巨大作用。一個有效 bug 模式檢測器的主要功能是發現更高百分比的可疑代碼,使該模式具有更大的使用價值。創建 bug 模式檢測器可以提高使用價值;創建檢測器之后,無論是現在還是將來,您都可以在您需要的任何代碼上運行它,并且您可能對發現的問題感到驚奇。例如,第 1 部分 中的簡單檢測器顯示了對 System.gc()
的調用,在 JDK 1.4.2 中,它隱藏在 JPEG 圖像 I/O 庫中。
編寫檢測器可以查找對特定靜態方法的調用,這并不困難,但是大多數的 bug 檢測器都包含相當多的分析和實現。在這一期的文章中,您將開發一個稱為 RuntimeException capture 的更小 bug 模式的檢測器(目前,FindBugs 發行版中已包含此 bug 檢測器。)
RuntimeException 捕捉
用 java™ 語言進行異常處理的一個優點是:異常是一些對象,try-catch
機制了解異常類型的分層結構,并在客戶機如何處理錯誤處理方面提供實際靈活性。例如,假如不能找到文件,則 FileInputStream
構造函數會拋出 FileNotFoundException
,該異常是 IOException
的一個子類。此傳統用法答應客戶機處理未發現文件的條件,這些條件是從其他與文件相關的條件中分離出來的(假如他們喜歡單獨捕捉 FileNotFoundException)。但是,他們還可以使用捕捉 IOException 的方法處理所有與文件相關的錯誤條件。
另一方面,異常處理的主要缺陷是:在正確使用異常時,易于建立帶有三行或四行業務邏輯以及 20 或 30 行異常處理的方法。因為錯誤恢復代碼在測試時輕易出現錯誤并且執行困難,使一部分專門用于異常處理的代碼無所適從并輕易出錯。這種情況的典型示例如清單所示,其中帶有兩行 “真的” 代碼的方法需要三個獨立的捕捉塊,每個捕捉塊都執行完全相同的操作 —— 記錄該異常:
public void addInstance(String className) { try { Class clazz = Class.forName(className); objectSet.add(clazz.newInstance()); } catch (IllegalaccessException e) { logger.log("Exception in addInstance", e); } catch (InstantiationException e) { logger.log("Exception in addInstance", e); } catch (ClassNotFoundException e) { logger.log("Exception in addInstance", e); }}
請參見清單 1,您可能嘗試將三個捕捉塊合并成捕捉 Exception
的單獨捕捉塊,因為每個捕捉塊的捕捉恢復操作是相同的。乍一看,該策略似乎是一個好方法 —— 但代碼副本有錯誤,所以整合這些復制路徑應該是一種改進。不過,此 “改進” 經常會帶來意想不到的結果。因為 RuntimeException
擴展了 Exception
,將三個捕捉塊合并成一個捕捉塊(如清單 2 所示),所以這會更改語義,現在,未經檢查的異常將被記錄(而不傳播)。此 bug 模式(其中 RuntimeException
輕易被超大捕捉塊捕捉)也稱為 RuntimeException 捕捉。
public void addInstance(String className) { try { Class clazz = Class.forName(className); objectSet.add(clazz.newInstance()); } catch (Exception e) { logger.log("Exception in newInstance", e); }}
bug 模式通常源自語言的模糊功能或類庫;出現此 bug 模式是因為 RuntimeException
擴展了 Exception
,這稍微有點違反常理。對 RuntimeException
捕捉的修復非常輕易 —— 您需要了解以下問題:首先捕捉 RuntimeException
,并在捕捉 Exception
之前重新將其拋出,如清單 3 所示。不過,即使知道 bug 模式及其修復方法,在代碼審查過程中也很輕易忘記執行它或忽略它,并且編譯器也不會通知您。這是引入 bug 模式的原因,幫助您避免違犯 “您已較好地了解” 之類的錯誤。
public void addInstance(String className) { try { Class clazz = Class.forName(className); objectSet.add(clazz.newInstance()); } catch (RuntimeException e) { throw e; } catch (Exception e) { logger.log("Exception in newInstance", e); }}
新聞熱點
疑難解答