標籤:變數定義 問題: 程式碼範例 copy jta 外掛程式 系統 length 黃色
摘自:http://www.oschina.net/question/129540_23043常用 Java 靜態程式碼分析工具的分析與比較
簡介: 本文首先介紹了靜態程式碼分析的基本概念及主要技術,隨後分別介紹了現有 4 種主流 Java 靜態程式碼分析工具 (Checkstyle,FindBugs,PMD,Jtest),最後從功能、特性等方面對它們進行分析和比較,希望能夠協助 Java 軟體開發人員瞭解靜態程式碼分析工具,並選擇合適的工具應用到軟體開發中。
引言
在 Java 軟體開發過程中,Team Dev往往要花費大量的時間和精力發現並修改代碼缺陷。Java 靜態程式碼分析(static code analysis)工具能夠在代碼構建過程中協助開發人員快速、有效定位代碼缺陷並及時糾正這些問題,從而極大地提高軟體可靠性並節省軟體開發與測試成 本。目前市場上的 Java 靜態程式碼分析工具種類繁多且各有千秋,因此本文將分別介紹現有 4 種主流 Java 靜態程式碼分析工具 (Checkstyle,FindBugs,PMD,Jtest),並從功能、特性等方面對它們進行分析和比較,希望能夠協助 Java 軟體開發人員瞭解靜態程式碼分析工具,並選擇合適的工具應用到軟體開發中。
靜態程式碼分析工具簡介
什麼是靜態程式碼分析
靜態程式碼分析是指無需運行被測代碼,僅通過分析或檢查來源程式的文法、結構、過程、介面等來檢查程式的正確性,找出程式碼後置的錯誤和缺陷,如參數不匹配,有歧義的嵌套語句,錯誤的遞迴,非法計算,可能出現的null 指標引用等等。
在軟體開發過程中,靜態程式碼分析往往先於動態測試之前進行,同時也可以作為制定動態測試案例的參考。統計證明,在整個軟體開發生命週期中,30% 至 70% 的代碼邏輯設計和編碼缺陷是可以通過靜態程式碼分析來發現和修複的。
但是,由於靜態程式碼分析往往要求大量的時間消耗和相關知識的積累,因此對於軟體Team Dev來說,使用靜態程式碼分析工具自動化執行代碼檢查和分析,能夠極大地提高軟體可靠性並節省軟體開發與測試成本。
靜態程式碼分析工具的優勢
1. 協助程式開發人員自動執行靜態程式碼分析,快速定位程式碼後置錯誤和缺陷。
2. 協助代碼設計人員更專註於分析和解決代碼設計缺陷。
3. 顯著減少在代碼逐行檢查上花費的時間,提高軟體可靠性並節省軟體開發與測試成本。
Java 靜態程式碼分析理論基礎和主要技術
- 缺陷模式比對:缺陷模式比對事先從程式碼分析經驗中收集足夠多的共性缺陷模式,將待分析代碼與已有的共性缺陷模式進行模式比對,從而完成軟體的安全分析。這種方式的優點是簡單方便,但是要求內建足夠多缺陷模式,且容易產生誤判。
- 類型推斷:類型推斷技術是指通過對代碼中運算物件類型進行推理,從而保證代碼中每條語句都針對正確的類型執行。這種技術首先將預定義一套類型機制,包括類 型等價、類型包含等推斷規則,而後基於這一規則進行推理計算。類型推斷可以檢查代碼中的類型錯誤,簡單,高效,適合代碼缺陷的快速檢測。
- 模型檢查:模型檢驗建立於有限狀態自動機的概念基礎之上,這一理論將被分析代碼抽象為一個自動機系統,並且假設該系統是有限狀態的、或者是可以通過抽象歸 結為有限狀態。模型檢驗過程中,首先將被分析代碼中的每條語句產生的影響抽象為一個有限狀態自動機的一個狀態,而後通過分析有限狀態機器從而達到程式碼分析的 目的。模型檢驗主要適合檢驗程式並發等時序特性,但是對於資料範圍資料類型等方面作用較弱。
- 資料流分析:資料流分析也是一種軟體驗證技術,這種技術通過收集代碼中引用到的變數資訊,從而分析變數在程式中的賦值、引用以及傳遞等情況。對資料流進行 分析可以確定變數的定義以及在代碼中被引用的情況,同時還能夠檢查代碼資料流異常,如引用在前賦值在後、只賦值無引用等。資料流分析主要適合檢驗程式中的 資料域特性。
現有主流 Java 靜態分析工具
Checkstyle
Checkstyle 是 SourceForge 的開源項目,通過檢查對代碼編碼格式,命名規範,Javadoc,類設計等方面進行代碼規範和風格的檢查,從而有效約束開發人員更好地遵循代碼編寫規範。
Checkstyle 提供了支援大多數常見 IDE 的外掛程式,文本主要使用 Eclipse 中的 Checkstyle 外掛程式。如 1 所示,Checkstyle 對代碼進行編碼風格檢查,並將檢查結果顯示在 Problems 視圖中。圖中,代碼編輯器中每個放大鏡表徵圖表示一個 Checkstyle 找到的代碼缺陷。開發人員可通過在 Problems 視圖中查看錯誤或警告詳細資料。
圖 1. 使用 Checkstyle 進行編碼風格檢查
此外,Checkstyle 支援使用者根據需求自訂代碼檢查規範,在 2 中的配置面板中,使用者可以在已有檢查規範如命名規範,Javadoc,塊,類設計等方面的基礎上添加或刪除自訂檢查規範。
圖 2. 使用 Checkstyle 添加自訂代碼檢查規範
FindBugs
FindBugs 是由馬里蘭大學提供的一款開源 Java 靜態程式碼分析工具。FindBugs 通過檢查類檔案或 JAR 檔案,將位元組碼與一組缺陷模式進行對比從而發現代碼缺陷,完成靜態程式碼分析。FindBugs 既提供可視化 UI 介面,同時也可以作為 Eclipse 外掛程式使用。文本將主要使用將 FindBugs 作為 Eclipse 外掛程式。在安裝成功後會在 eclipse 中增加 FindBugs perspective,使用者可以對指定 Java 類或 JAR 檔案運行 FindBugs,此時 FindBugs 會遍曆指定檔案,進行靜態程式碼分析,並將程式碼分析結果顯示在 FindBugs perspective 的 bugs explorer 中,如 3 所示:
圖 3. 使用 FindBugs 進行靜態程式碼分析
圖中 Bug Explorer 中的灰色表徵圖處為 Bug 類型,每種分類下紅色表徵圖表示 bug 較為嚴重,黃色的表徵圖表示 bug 為警告程度。Propreties 列出了 bug 的描述資訊及修改方案。
此外,FindBugs 還為使用者提供定製 Bug Pattern 的功能。使用者可以根據需求自訂 FindBugs 的代碼檢查條件,如 4 所示:
圖 4. 使用 FindBugs 添加自訂代碼檢查規範
PMD
PMD 是由 DARPA 在 SourceForge 上發布的開源 Java 代碼靜態分析工具。PMD 通過其內建的編碼規則對 Java 代碼進行靜態檢查,主要包括對潛在的 bug,未使用的代碼,重複的代碼,迴圈體建立新對象等問題的檢驗。PMD 提供了和多種 Java IDE 的整合,例如 Eclipse,IDEA,NetBean 等。本文主要使用 PMD 以外掛程式方式與 Eclipse 整合。如 5 所示:在 Violations Overview 視圖中,按照代碼缺陷嚴重性集中顯示了 PMD 靜態程式碼分析的結果。
圖 5. 使用 PMD 進行靜態程式碼分析
PMD 同樣也支援開發人員對代碼檢查規範進行自訂配置。開發人員可以在 6 中的面板中添加、刪除、匯入、匯出代碼檢查規範。
圖 6. 使用 PMD 添加自訂代碼檢查規範
Jtest
Jtest 是 Parasoft 公司推出的一款針對 Java 語言的自動化代碼最佳化和測試載入器,Jtest 的靜態程式碼分析功能能夠按照其內建的超過 800 條的 Java 編碼規範自動檢查並糾正這些隱蔽且難以修複的編碼錯誤。同時,還支援使用者自訂編碼規則,協助使用者預防一些特殊用法的錯誤。Jtest 提供了基於 Eclipse 的外掛程式安裝。Jtest 支援開發人員對 Java 代碼進行編碼規範檢查,並在 Jtask 視窗中集中顯示檢查結果,如 7 所示:
圖 7. 使用 Jtest 進行靜態程式碼分析
同時,Jtest 還提供了對使用者定製代碼檢查配置甚至自訂編碼規則的支援,這一功能使得開發人員可以基於不同情境定製所需要的編碼規範, 8 所示:
圖 8. 使用 Jtest 添加自訂代碼檢查規範
Java 靜態分析工具對比
本章節將從以下幾個方面對上述 Java 靜態分析工具進行比較:
應用技術及分析對象
下表 1 列出了不同工具的分析對象及應用技術對比:
表 1. 不同工具的分析對象及應用技術對比
| Java 靜態分析工具 |
分析對象 |
應用技術 |
| Checkstyle |
Java 源檔案 |
缺陷模式比對 |
| FindBugs |
位元組碼 |
缺陷模式比對;資料流分析 |
| PMD |
Java 原始碼 |
缺陷模式比對 |
| Jtest |
Java 原始碼 |
缺陷模式比對;資料流分析 |
內建編程規範
Checkstyle:
- Javadoc 注釋:檢查類及方法的 Javadoc 注釋
- 命名規範:檢查命名是否符合命名規範
- 標題:檢查檔案是否以某些行開頭
- Import 語句:檢查 Import 語句是否符合定義規範
- 代碼塊大小,即檢查類、方法等代碼塊的行數
- 空白:檢查空白符,如 tab,斷行符號符等
- 修飾符:修飾符號的檢查,如修飾符的定義順序
- 塊:檢查是否有空塊或無效塊
- 代碼問題:檢查重複代碼,條件判斷,魔數等問題
- 類設計:檢查類的定義是否符合規範,如建構函式的定義等問題
FindBugs:
- Bad practice 壞的實踐:常見代碼錯誤,用於靜態代碼檢查時進行缺陷模式比對
- Correctness 可能導致錯誤的代碼,如null 指標引用等
- 國際化相關問題:如錯誤的字串轉換
- 可能受到的惡意攻擊,如存取權限修飾符的定義等
- 多線程的正確性:如多線程編程時常見的同步,線程調度問題。
- 運行時效能問題:如由變數定義,方法調用導致的代碼低效問題。
PMD:
- 可能的 Bugs:檢查潛在代碼錯誤,如空 try/catch/finally/switch 語句
- 未使用代碼(Dead code):檢查未使用的變數,參數,方法
- 複雜的運算式:檢查不必要的 if 語句,可被 while 替代的 for 迴圈
- 重複的代碼:檢查重複的代碼
- 迴圈體建立新對象:檢查在迴圈體內執行個體化新對象
- 資源關閉:檢查 Connect,Result,Statement 等資源使用之後是否被關閉掉
Jtest
- 可能的錯誤:如記憶體破壞、記憶體泄露、指標錯誤、庫錯誤、邏輯錯誤和演算法錯誤等
- 未使用代碼:檢查未使用的變數,參數,方法
- 初始化錯誤:記憶體配置錯誤、變數初始化錯誤、變數定義衝突
- 命名規範:檢查命名是否符合命名規範
- Javadoc 注釋:檢查類及方法的 Javadoc 注釋
- 線程和同步:檢驗多線程編程時常見的同步,線程調度問題
- 國際化問題:
- 記憶體回收:檢查變數及 JDBC 資源是否存在記憶體泄露隱患
錯誤檢查能力
為比較上述 Java 靜態分析工具的代碼缺陷檢測能力,本文將使用一段範例程式碼進行實驗,範例程式碼中將涵蓋我們開發中的幾類常見錯誤,如引用操作、對象操作、運算式複雜化、數 組使用、未使用變數或程式碼片段、資源回收、方法調用及代碼設計幾個方面。最後本文將分別記錄在預設檢查規範設定下,不同工具對該範例程式碼的分析結果。以下為 範例程式碼 Test.java。其中,代碼的注釋部分列舉了代碼中可能存在的缺陷。
清單 1. Test.java 範例程式碼
package Test;import java.io.*;public class Test {/** * Write the bytes from input stream to output stream. * The input stream and output stream are not closed. * @param is * @param os * @throws IOException */public boolean copy(InputStream is, OutputStream os) throws IOException { int count = 0; //缺少null 指標判斷 byte[] buffer = new byte[1024];while ((count = is.read(buffer)) >= 0) {os.write(buffer, 0, count);}//未關閉I/O流return true;}/** * * @param a * @param b * @param ending * @return copy the elements from a to b, and stop when meet element ending */public void copy(String[] a, String[] b, String ending){int index;String temp = null;//null 指標錯誤System.out.println(temp.length());//未使用變數int length=a.length;for(index=0; index&a.length; index++){//多餘的if語句if(true){//對象比較 應使用equalsif(temp==ending){break;}//缺少 數組下標越界檢查b[index]=temp;}}}/** * * @param file * @return file contents as string; null if file does not exist */public void readFile(File file) {InputStream is = null;OutputStream os = null;try {is = new BufferedInputStream(new FileInputStream(file));os = new ByteArrayOutputStream();//未使用方法傳回值copy(is,os);is.close();os.close();} catch (IOException e) {//可能造成I/O流未關閉e.printStackTrace();} finally{//空的try/catch/finally塊}}}
通過以上測試代碼,我們對已有 Java 靜態程式碼分析工具的檢驗結果做了如下比較,如下表 2 所示。
表 2. Java 靜態程式碼分析工具對比
| 代碼缺陷分類 |
樣本 |
Checkstyle |
FindBugs |
PMD |
Jtest |
| 引用操作 |
null 指標引用 |
√ |
√ |
√ |
√ |
| 對象操作 |
對象比較(使用 == 而不是 equals) |
|
√ |
√ |
√ |
| 運算式複雜化 |
多餘的 if 語句 |
|
|
√ |
|
| 數組使用 |
數組下標越界 |
|
|
|
√ |
| 未使用變數或程式碼片段 |
未使用變數 |
|
√ |
√ |
√ |
| 資源回收 |
I/O 未關閉 |
|
√ |
|
√ |
| 方法調用 |
未使用方法傳回值 |
|
√ |
|
|
| 代碼設計 |
空的 try/catch/finally 塊 |
|
|
√ |
|
由表中可以看出幾種工具對於代碼檢查各有側重。其中,Checkstyle 更偏重於代碼編寫格式,及是否符合編碼規範的檢驗,對代碼 bug 的發現功能較弱;而 FindBugs,PMD,Jtest 著重於發現代碼缺陷。在對代碼缺陷檢查中,這三種工具在針對的代碼缺陷類別也各有不同,且類別之間有重疊。
總結
本文分別從功能、特性和內建編程規範等方面詳細介紹了包括 Checkstyle,FindBugs,PMD,Jtest 在內的四種主流 Java 靜態程式碼分析工具,並通過一段 Java 程式碼範例對這四種工具的程式碼分析能力進行比較。由於這四種工具內建編程規範各有不同,因此它們對不同種類的代碼問題的發現能力也有所不同。其中 Checkstyle 更加偏重於代碼編寫格式檢查,而 FindBugs,PMD,Jtest 著重於發現代碼缺陷。最後,希望本文能夠協助 Java 軟體開發與測試人員進一步瞭解以上四種主流 Java 靜態分析工具,並協助他們根據需求選擇合適的工具。
【轉載】常用 Java 靜態程式碼分析工具的分析與比較