今天代碼質量再次強調java代碼提交SVN前要經過findBugs檢查,雖然根據菜單我也基本會有findBugs插件,但為了更全面的學習、更高效的利用,我搜索學習了findbugs的用法。
檢查原理
Findbugs是一個靜態分析工具,它檢查類或者JAR 文件,將字節碼與一組缺陷模式進行對比以發現可能的問題。Findbugs自帶檢測器,其中有60余種Bad practice,80余種Correctness,1種 Internationalization,12種Malicious code vulnerability,27種Multithreaded correctness,23種Performance,43種Dodgy。我們還可以自己配置檢查規則(做哪些檢查,不做哪些檢查),也可以自己來實現獨有的校驗規則(用戶自定義特定的bug模式需要繼承它的接口,編寫自己的校驗類,屬於高級技巧)。
白盒測試中的靜態檢查一般是檢查編碼標准規范,錯誤列表。編碼規范往往團隊會根據自己的經驗和風格進行設置一些規范。現在很多IDE工具都會在編輯代碼的時候實時的提醒是否符合代碼風格。錯誤列表,一般是代碼潛在的bug,由於某種代碼寫法雖然沒有語法錯誤,但是可能存在錯誤,比如會導致線程死鎖。這些都是錯誤列表應該檢查的。靜態檢查的可操作方式:
1、代碼走查:
程序員之間可以隔一定的時間抽取代碼進行走查。
走查的時候根據匯總報告,把這些經驗匯成列表,作為下次代碼走查的依據。
該方式的特點是,手工、多人討論、操作簡單,但是效率會比較低。
2、代碼掃描
使用軟件對我們的代碼進行掃描,查找出潛在的問題。現在有許多商業的工具能夠進行掃描,比如Parasoft JTest、Software Analyzer、pclint等工具,往往不同的工具會針對不同的語言。當然也有很多開源的工具。在這里java方面主要推薦Findbugs。Findbugs可以在ANT/GUI/ECLIPSE三個環境中運行,同時也可以編寫自己的檢測器,功能比較完善。我們平時可以收集自己的或者是別人的開發經驗,把它做成檢測器來完善Findbugs的檢測體系。軟件掃描的特點是,機器掃描、效率高,但是沒不夠靈活,擴展比較負責。
Java靜態檢查工具對比
參考:http://blog.csdn.net/ml5271169588/article/details/6975701
http://www.cnblogs.com/hyddd/archive/2008/12/16/1356310.html
工具 |
目的 |
檢查項 |
FindBugs 檢查.class |
基於Bug Patterns概念,查找javabytecode(.class文件)中的潛在bug |
主要檢查bytecode中的bug patterns,如NullPoint空指針檢查、沒有合理關閉資源、字符串相同判斷錯(==,而不是equals)等 |
PMD 檢查源文件 |
檢查Java源文件中的潛在問題 |
主要包括: 空try/catch/finally/switch語句塊 未使用的局部變量、參數和private方法 空if/while語句 過於復雜的表達式,如不必要的if語句等 復雜類 |
CheckStyle 檢查源文件 主要關注格式 |
檢查Java源文件是否與代碼規范相符 |
主要包括: Javadoc注釋 命名規范 多余沒用的Imports Size度量,如過長的方法 缺少必要的空格Whitespace 重復代碼 |
使用及配置
摘自:http://developer.51cto.com/art/200906/127165.htm
本文主要介紹在Eclipse中使用的情況
FindBugs是一個可以在Java程序中發現Bugs的程序。它是專門用來尋找處於"Bug Patterns"列表中的代碼的。Bug Patterns指很有可能是錯誤的代碼的實例。
打開Bug Details視圖Windows => Show View => Other… => FindBugs => BugDetails
在Package Explorer或Navigator視圖中,選中你的Java項目,右鍵,可以看到"Find Bugs"菜單項,子菜單項里有"Find Bugs"和"Clear Bug Markers"兩項內容,如下圖所示:
我們建立一個簡單的測試文件Test.java 內容如下:
public class Test { private String[] name; public String[] getName() { return name; } public void setName(String[] name) { this.name = name; } } |
我們點中"Find Bugs",運行時會出現如下進度框:
運行結束后可以在Problems中看到增加了如下的警告信息內容
FindBugs運行后的警告信息內容不僅在Problems視圖中顯示,而且將標記在源代碼標記框中,在源代碼編輯器中我們可以看到警告標識,如下圖:
當光標指向你的警告信息的代碼上面時,就會有相應的錯誤提示信息,與Eclipse本身的錯誤或警告信息提示類似。
選中Problems視圖里出現的相應問題,就會在代碼編輯器里切換到相應的代碼上去,方便根據相應的提示信息進行代碼的修改。
在Problems視圖里,選中相應的問題條目,右鍵,在彈出的菜單中,可以看到"Show Bug Details",如下圖所示:
點中它,會切換到Bug Details視圖上去,顯示更加詳細的提示信息。
當然,在代碼編輯窗口中,點擊帶有警告提示信息的圖標時,也會自動切換到Bud Details窗口去,查看詳細的警告信息,如下圖所示。
根據這里詳細的信息,你可以得到FindBugs為什么會對你的代碼報警告信息,及相應的處理辦法,根據它的提示,你可以快速方便地進行代碼修改。
根據提示,我們將代碼修改成如下,再運行就不會報有警告信息了。
public class Test { private String[] name; public String[] getName() { String[] temp = name; return temp; } public void setName(String[] name) { String[] temp = name; this.name = temp; } } |
配置FindBugs
選擇你的項目,右鍵 => Properties => FindBugs =>
可以配置的信息包括如上圖所示的四個選項的相關設置:
1. Run FindBugs Automatically開關
當此項選中后,FindBugs將會在你修改Java類時自動運行,如你設置了Eclipse自動編譯開關后,當你修改完Java文件保存,FindBugs就會運行,並將相應的信息顯示出來。
當此項沒有選中,你只能每次在需要的時候自己去運行FindBugs來檢查你的代碼。
2. Minimum priority to report選擇項
這個選擇項是讓你選擇哪個級別的信息進行顯示,有Low、Medium、High三個選擇項可以選擇,很類似於Log4J的級別設置啦。 比如:
你選擇了High選擇項,那么只有是High級別的提示信息才會被顯示。
你選擇了Medium選擇項,那么只有是Medium和High級別的提示信息才會被顯示。
你選擇了Low選擇項,那么所有級別的提示信息都會被顯示。
3. Enable bug categories選擇項
在這里是一些顯示Bug分類的選擇:
Correctness關於代碼正確性相關方面的
Performance關於代碼性能相關方面的
Internationalization關於代碼國際化相關方面的
Multithreaded correctness關於代碼多線程正確性相關方面的
Style關於代碼樣式相關方面的
Malicious code vulnerability關於惡意破壞代碼相關方面的
比如:如果你把Style的檢查框去掉不選擇中它,那么與Style分類相關的警告信息就不會顯示了。其它的類似。
4. Select bug patterns to check for選擇項
在這里你可以選擇所要進行檢查的相關的Bug Pattern條目
可以從Bug codes、Detector name、Detector description中看到相應的是要檢查哪些方面的內容,你可以根據需要選擇或去掉相應的 檢查條件。
三、詳細說明
Findbugs是一個靜態分析工具,它檢查類或者JAR 文件,將字節碼與一組缺陷模式進行對比以發現可能的問題。Findbugs自帶檢測器,其中有60余種Bad practice,80余種Correctness,1種 Internationalization,12種Malicious code vulnerability,27種Multithreaded correctness,23種Performance,43種Dodgy。
Bad practice 壞的實踐
一些不好的實踐,下面列舉幾個:
HE: 類定義了equals(),卻沒有hashCode();或類定義了equals(),卻使用
Object.hashCode();或類定義了hashCode(),卻沒有equals();或類定義了hashCode(),卻使用Object.equals();類繼承了equals(),卻使用Object.hashCode()。
SQL:Statement 的execute方法調用了非常量的字符串;或Prepared Statement是由一個非常量的字符串產生。
DE: 方法終止或不處理異常,一般情況下,異常應該被處理或報告,或被方法拋出。
Correctness 一般的正確性問題
可能導致錯誤的代碼,下面列舉幾個:
NP: 空指針被引用;在方法的異常路徑里,空指針被引用;方法沒有檢查參數是否null;null值產生並被引用;null值產生並在方法的異常路徑被引用;傳給方法一個聲明為@NonNull的null參數;方法的返回值聲明為@NonNull實際是null。
Nm: 類定義了hashcode()方法,但實際上並未覆蓋父類Object的hashCode();類定義了tostring()方法,但實際上並未覆蓋父類Object的toString();很明顯的方法和構造器混淆;方法名容易混淆。
SQL:方法嘗試訪問一個Prepared Statement的0索引;方法嘗試訪問一個ResultSet的0索引。
UwF:所有的write都把屬性置成null,這樣所有的讀取都是null,這樣這個屬性是否有必要存在;或屬性從沒有被write。
Internationalization 國際化
當對字符串使用upper或lowercase方法,如果是國際的字符串,可能會不恰當的轉換。
Malicious code vulnerability 可能受到的惡意攻擊
如果代碼公開,可能受到惡意攻擊的代碼,下面列舉幾個:
FI: 一個類的finalize()應該是protected,而不是public的。
MS:屬性是可變的數組;屬性是可變的Hashtable;屬性應該是package protected的。
Multithreaded correctness 多線程的正確性
多線程編程時,可能導致錯誤的代碼,下面列舉幾個:
ESync:空的同步塊,很難被正確使用。
MWN:錯誤使用notify(),可能導致IllegalMonitorStateException異常;或錯誤的
使用wait()。
No: 使用notify()而不是notifyAll(),只是喚醒一個線程而不是所有等待的線程。
SC: 構造器調用了Thread.start(),當該類被繼承可能會導致錯誤。
Performance 性能問題
可能導致性能不佳的代碼,下面列舉幾個:
DM:方法調用了低效的Boolean的構造器,而應該用Boolean.valueOf(…);用類似
Integer.toString(1) 代替new Integer(1).toString();方法調用了低效的float的構造器,應該用靜態的valueOf方法。
SIC:如果一個內部類想在更廣泛的地方被引用,它應該聲明為static。
SS: 如果一個實例屬性不被讀取,考慮聲明為static。
UrF:如果一個屬性從沒有被read,考慮從類中去掉。
UuF:如果一個屬性從沒有被使用,考慮從類中去掉。
Dodgy 危險的
具有潛在危險的代碼,可能運行期產生錯誤,下面列舉幾個:
CI: 類聲明為final但聲明了protected的屬性。
DLS:對一個本地變量賦值,但卻沒有讀取該本地變量;本地變量賦值成null,卻沒有讀取該本地變量。
ICAST: 整型數字相乘結果轉化為長整型數字,應該將整型先轉化為長整型數字再相乘。
INT:沒必要的整型數字比較,如X <= Integer.MAX_VALUE。
NP: 對readline()的直接引用,而沒有判斷是否null;對方法調用的直接引用,而方法可能返回null。
REC:直接捕獲Exception,而實際上可能是RuntimeException。
ST: 從實例方法里直接修改類變量,即static屬性。
可參考:http://blog.csdn.net/fanyuna/article/details/6860198