FindBugs的使用

  • 2020 年 4 月 10 日
  • 筆記

FindBugs的使用

1 FindBugs簡介

FindBugs是一個靜態分析工具,它檢查類或者JAR文件,將位元組碼與一組缺陷模式進行對比以發現可能的問題。有了靜態分析工具,就可以在不實際運行程式的情況對軟體進行分析。FindBugs有幾種,有的為單機程式版,有的為與Eclipse相結合的插件版,等等。本文介紹的就是Java程式設計師最喜歡的插件版。

2 FindBugs的安裝

在網上下載FindBugs插件,解壓後,直接將解壓後的文件(edu.umd.cs.findbugs.plugin.eclipse_3.0.1.20150306-5afe4d1)放到eclipse的dropins目錄,然後重啟eclipse即可。右鍵點擊項目,看到Find Bugs出現就可以了。

3 FindBugs在Eclipse的使用

在需要靜態檢查的項目上點擊右鍵,按照上圖所示,點擊Find Bugs,等待靜態檢查進度完成。

小瓢蟲所在位置即為問題程式碼所在位置。

檢測出的bugs可以到《詳解FindBugs的各項檢測器》中查找對應原因。

4 FindBugs可以檢測的內容

FindBugs提供了35個檢測器來檢測位元組碼中可能的缺陷。其可以做的事情主要有:

4.1 找出hash equals不匹配

找與equals()和hashCode()的實現相關的幾個問題。這兩個方法非常重要,因為幾乎所有基於集合的類—List、Map、Set等都調用它們。一般來說,這個檢測器尋找兩種不同類型的問題:

①當一個類重寫對象的equals()方法,但是沒有重寫它的hashCode方法,或者相反的情況時。

②定義一個co-variant版本的equals()或compareTo()方法。例如,Bob類定義其equals()方法為布爾equals(Bob),它覆蓋了對象中定義的equals()方法。因為Java程式碼在編譯時解析重載方法的方式,在運行時使用的幾乎總是在對象中定義的這個版本的方法,而不是在Bob中定義的那一個(除非顯式將equals()方法的參數強制轉換為Bob類型)。因此,當這個類的一個實例放入到類集合中的任何一個中時,使用的是Object.equals()版本的方法,而不是在Bob中定義的版本。在這種情況下,Bob類應當定義一個接受類型為Object的參數的equals()方法。

4.2檢測:忽略方法返回值

這個檢測器查找程式碼中忽略了不應該忽略的方法返回值的地方。這種情況的一個常見例子是在調用String方法時,例如:

1 String aString ="bob";

2 b.replace('b', 'p');

3 if(b.equals("pop"))

這個錯誤很常見。在第2行,程式設計師認為他已經用p替換了字元串中的所有b。確實是這樣,但是他忘記了字元串是不可變的。所有這類方法都返回一個新字元串,而從來不會改變消息的接收者。

4.3檢測:Null指針對null的解引用(dereference)和冗餘比較

這個檢測器查找兩類問題。它查找程式碼路徑將會或者可能造成null指針異常的情況,它還查找對null的冗餘比較的情況。例如,如果兩個比較值都為null,那麼它們就是冗餘的並可能表明程式碼錯誤。FindBugs在可以確定一個值為null而另一個值不為null時,檢測類似的錯誤,例如:

1 Person person =aMap.get("bob");

2 if (person != null) {

3 person.updateAccessTime();

4 }

5 String name =person.getName();

在這個例子中,如果第1行的Map不包括一個名為「bob」的人,那麼在第5行詢問person的名字時就會出現null指針異常。因為FindBugs不知道map是否包含「bob」,所以它將第5行標記為可能null指針異常。

4.4檢測:初始化之前讀取欄位

這個檢測器尋找在構造函數中初始化之前被讀取的欄位。這個錯誤通常是由使用欄位名而不是構造函數參數引起的,例如在構造函數中讀取未初始化的欄位:

1 public class Thing {

2 private List actions;

3 public Thing(StringstartingActions) {

4 StringTokenizertokenizer =

5 newStringTokenizer(startingActions);

6 while(tokenizer.hasMoreTokens()) {

7 actions.add(tokenizer.nextToken());

8 }

9 }

10 }

在這個例子中,第7行將產生一個null指針異常,因為變數actions還沒有初始化。

4.5命名檢查

對標準Java命令規範的測試:變數名稱不應太短;方法名稱不應過長;類名稱應當以小寫字母開頭;方法和欄位名應當以小寫字母開頭,等等。

4.6未使用的程式碼檢查

查找從未使用的私有欄位和本地變數、執行不到的語句、從未調用的私有方法,等等。

4.7嵌套檢查

例如:switch語句應當有default塊,應當避免深度嵌套的if塊,不應當給參數重新賦值,不應該對double值進行相等比較。

4.8導入語句檢查

檢查import語句的問題,比如同一個類被導入兩次或者被導入java.lang的類中。

4.9JUnit測試檢查

查找測試用例和測試方法的特定問題,例如方法名稱的正確拼寫,以及suite()方法是不是static和public。

4.10字元串檢查

找出處理字元串時遇到的常見問題,例如重複的字元串標量,調用String構造函數,對String變數調用toString()方法。

4.11括弧檢查

檢查for、if、while和else語句是否使用了括弧。

4.12程式碼尺寸檢查

測試過長的方法、有太多方法的類以及重構方面的類似問題。

4.13終結函數檢查

因為在Java語言中,finalize()方法不是那麼普遍,它們的使用規則雖然很詳細,但是人們對它們相對不是很熟悉。這類檢查查找finalize()方法的各種問題,例如空的終結函數,調用其他方法的finalize()方法,對finalize()的顯式調用,等等。

4.14克隆檢查

用於clone()方法的新規則。凡是重寫clone()方法的類都必須實現Cloneable,clone()方法應該調用super.clone(),而clone()方法應該聲明拋出CloneNotSupportedException異常,即使實際上沒有拋出異常,也要如此。

4.15耦合檢查

查找類之間過度耦合的跡象,比如導入內容太多;在超類型或介面就已經夠用的時候使用子類的類型;類中的欄位、變數和返回類型過多等。

4.16異常檢查

針對異常的檢查:不應該聲明該方法而拋出java.lang.Exception異常,不應當將異常用於流控制,不應該捕獲Throwable,等等。

4.17日誌檢查

查找java.util.logging.Logger的不當使用,包括非終狀態(nonfinal)、非靜態的記錄器,以及在一個類中有多個記錄器。

4.18Open—Close檢查

檢查文件或通訊方面,是否忘記Close的情況。

4.19其它檢查

其它缺陷清單可參見:缺陷清單

4.20構建自己的規則集

可以構建自己的規則集。