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構建自己的規則集
可以構建自己的規則集。