1. 簡(jiǎn)介與引言
1.1 簡(jiǎn)介
本文首先介紹了靜態(tài)代碼分析的基本概念及主要技術(shù)蚌讼,隨后分別介紹了現(xiàn)有 4 種主流 Java 靜態(tài)代碼分析工具 (Checkstyle阴汇,F(xiàn)indBugs,PMD,Jtest),最后從功能泻帮、特性等方面對(duì)它們進(jìn)行分析和比較,希望能夠幫助 Java 軟件開(kāi)發(fā)人員了解靜態(tài)代碼分析工具友酱,并選擇合適的工具應(yīng)用到軟件開(kāi)發(fā)中晴音。
1.2 引言
在Java軟件開(kāi)發(fā)過(guò)程中,開(kāi)發(fā)團(tuán)隊(duì)往往要花費(fèi)大量的時(shí)間和精力發(fā)現(xiàn)并修改代碼缺陷缔杉。Java 靜態(tài)代碼分析(static code analysis)工具能夠在代碼構(gòu)建過(guò)程中幫助開(kāi)發(fā)人員快速锤躁、有效的定位代碼缺陷并及時(shí)糾正這些問(wèn)題,從而極大地提高軟件可靠性并節(jié)省軟件開(kāi)發(fā)和測(cè)試成本或详。目前市場(chǎng)上的 Java 靜態(tài)代碼分析工具種類繁多且各有千秋系羞,因此本文將分別介紹現(xiàn)有4種主流Java靜態(tài)代碼分析工具 (Checkstyle,F(xiàn)indBugs霸琴,PMD椒振,Jtest),并從功能梧乘、特性等方面對(duì)它們進(jìn)行分析和比較澎迎,希望能夠幫助 Java 軟件開(kāi)發(fā)人員了解靜態(tài)代碼分析工具,并選擇合適的工具應(yīng)用到軟件開(kāi)發(fā)中选调。
2. 靜態(tài)代碼分析工具簡(jiǎn)介
2.1 什么是靜態(tài)代碼分析
靜態(tài)代碼分析是指無(wú)需運(yùn)行被測(cè)代碼夹供,僅通過(guò)分析或檢查源程序的語(yǔ)法、結(jié)構(gòu)仁堪、過(guò)程哮洽、接口等來(lái)檢查程序的正確性,找出代碼隱藏的錯(cuò)誤和缺陷弦聂,如參數(shù)不匹配鸟辅,有歧義的嵌套語(yǔ)句,錯(cuò)誤的遞歸莺葫,非法計(jì)算匪凉,可能出現(xiàn)的空指針引用等等。在軟件開(kāi)發(fā)過(guò)程中捺檬,靜態(tài)代碼分析往往先于動(dòng)態(tài)測(cè)試之前進(jìn)行洒缀,同時(shí)也可以作為制定動(dòng)態(tài)測(cè)試用例的參考。統(tǒng)計(jì)證明欺冀,在整個(gè)軟件開(kāi)發(fā)生命周期中树绩,30%至70%的代碼邏輯設(shè)計(jì)和編碼缺陷是可以通過(guò)靜態(tài)代碼分析來(lái)發(fā)現(xiàn)和修復(fù)的。但是隐轩,由于靜態(tài)代碼分析往往要求大量的時(shí)間消耗和相關(guān)知識(shí)的積累饺饭,因此對(duì)于軟件開(kāi)發(fā)團(tuán)隊(duì)來(lái)說(shuō),使用靜態(tài)代碼分析工具自動(dòng)化執(zhí)行代碼檢查和分析职车,能夠極大地提高軟件可靠性并節(jié)省軟件開(kāi)發(fā)和測(cè)試成本瘫俊。
2.2 靜態(tài)代碼分析工具的優(yōu)勢(shì)
1. 幫助程序開(kāi)發(fā)人員自動(dòng)執(zhí)行靜態(tài)代碼分析鹊杖,快速定位代碼隱藏錯(cuò)誤和缺陷。
2. 幫助代碼設(shè)計(jì)人員更專注于分析和解決代碼設(shè)計(jì)缺陷扛芽。
3. 顯著減少在代碼逐行檢查上花費(fèi)的時(shí)間骂蓖,提高軟件可靠性并節(jié)省軟件開(kāi)發(fā)和測(cè)試成本。
2.3 Java靜態(tài)代碼分析理論基礎(chǔ)和主要技術(shù)
2.3.1 分缺陷模式匹配
缺陷模式匹配事先從代碼分析經(jīng)驗(yàn)中收集足夠多的共性缺陷模式川尖,將待分析代碼與已有的共性缺陷模式進(jìn)行模式匹配登下,從而完成軟件的安全分析。這種方式的優(yōu)點(diǎn)是簡(jiǎn)單方便叮喳,但是要求內(nèi)置足夠多缺陷模式被芳,且容易產(chǎn)生誤報(bào)。
2.3.2 類型推斷
類型推斷技術(shù)是指通過(guò)對(duì)代碼中運(yùn)算對(duì)象類型進(jìn)行推理馍悟,從而保證代碼中每條語(yǔ)句都針對(duì)正確的類型執(zhí)行畔濒。這種技術(shù)首先將預(yù)定義一套類型機(jī)制,包括類 型等價(jià)锣咒、類型包含等推理規(guī)則侵状,而后基于這一規(guī)則進(jìn)行推理計(jì)算。類型推斷可以檢查代碼中的類型錯(cuò)誤毅整,簡(jiǎn)單趣兄,高效,適合代碼缺陷的快速檢測(cè)毛嫉。
2.3.3 模型檢查
模型檢驗(yàn)建立于有限狀態(tài)自動(dòng)機(jī)的概念基礎(chǔ)之上诽俯,這一理論將被分析代碼抽象為一個(gè)自動(dòng)機(jī)系統(tǒng)妇菱,并且假設(shè)該系統(tǒng)是有限狀態(tài)的承粤、或者是可以通過(guò)抽象歸 結(jié)為有限狀態(tài)。模型檢驗(yàn)過(guò)程中闯团,首先將被分析代碼中的每條語(yǔ)句產(chǎn)生的影響抽象為一個(gè)有限狀態(tài)自動(dòng)機(jī)的一個(gè)狀態(tài)辛臊,而后通過(guò)分析有限狀態(tài)機(jī)從而達(dá)到代碼分析的 目的。模型檢驗(yàn)主要適合檢驗(yàn)程序并發(fā)等時(shí)序特性房交,但是對(duì)于數(shù)據(jù)值域數(shù)據(jù)類型等方面作用較弱彻舰。
2.3.4 數(shù)據(jù)流分析
數(shù)據(jù)流分析也是一種軟件驗(yàn)證技術(shù),這種技術(shù)通過(guò)收集代碼中引用到的變量信息候味,從而分析變量在程序中的賦值刃唤、引用以及傳遞等情況。對(duì)數(shù)據(jù)流進(jìn)行 分析可以確定變量的定義以及在代碼中被引用的情況白群,同時(shí)還能夠檢查代碼數(shù)據(jù)流異常尚胞,如引用在前賦值在后、只賦值無(wú)引用等帜慢。數(shù)據(jù)流分析主要適合檢驗(yàn)程序中的 數(shù)據(jù)域特性笼裳。
3. 現(xiàn)有主流Java靜態(tài)分析工具
3.1 Checkstyle
3.1.1 簡(jiǎn)介
Checkstyle是SourceForge的開(kāi)源項(xiàng)目唯卖,通過(guò)檢查對(duì)代碼編碼格式,命名約定躬柬,Javadoc拜轨,類設(shè)計(jì)等方面進(jìn)行代碼規(guī)范和風(fēng)格的檢查,從而有效約束開(kāi)發(fā)人員更好地遵循代碼編寫(xiě)規(guī)范允青。Checkstyle 提供了支持大多數(shù)常見(jiàn)IDE的插件橄碾,文本主要使用IntelliJ IDEA中的CheckStyle-IDEA插件。
3.1.2 內(nèi)置編程規(guī)范
Javadoc 注釋:檢查類及方法的 Javadoc 注釋
命名約定:檢查命名是否符合命名規(guī)范
標(biāo)題:檢查文件是否以某些行開(kāi)頭
Import 語(yǔ)句:檢查 Import 語(yǔ)句是否符合定義規(guī)范
代碼塊大小昧廷,即檢查類堪嫂、方法等代碼塊的行數(shù)
空白:檢查空白符,如 tab木柬,回車符等
修飾符:修飾符號(hào)的檢查皆串,如修飾符的定義順序
塊:檢查是否有空塊或無(wú)效塊
代碼問(wèn)題:檢查重復(fù)代碼,條件判斷眉枕,魔數(shù)等問(wèn)題
類設(shè)計(jì):檢查類的定義是否符合規(guī)范恶复,如構(gòu)造函數(shù)的定義等問(wèn)題
3.1.3 在idea中集成CheckStyle
File->Setting->Plugins至下圖界面,搜索CheckStyle-IDEA速挑,點(diǎn)擊安裝谤牡。
3.1.4 在idea中使用CheckStyle
第一步,使CheckStyle在idea中生效
settings->Editor->Inspections->CheckStyle
第二步姥宝,添加配置文件翅萤,即為CheckStyle配置檢測(cè)的規(guī)范,設(shè)定需要的代碼規(guī)范
以下是配置文件的一個(gè)樣本:
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">
<!--
If you set the basedir property below, then all reported file
names will be relative to the specified directory. See
http://checkstyle.sourceforge.net/5.x/config.html#Checker
<property name="basedir" value="${basedir}"/>
-->
<!-- 檢查每個(gè)包中是否有java注釋文件腊满,默認(rèn)有package-info.java -->
<!-- <module name="JavadocPackage"/> -->
<!-- 檢查文件是否以一個(gè)空行結(jié)束 -->
<module name="NewlineAtEndOfFile"/>
<!-- 檢查property文件中是否有相同的key -->
<module name="Translation"/>
<!-- 文件長(zhǎng)度不超過(guò)1500行 -->
<module name="FileLength">
<property name="max" value="1500"/>
</module>
<!-- 檢查文件中是否含有'\t' -->
<module name="FileTabCharacter"/>
<!-- Miscellaneous other checks. -->
<module name="RegexpSingleline">
<property name="format" value="\s+$"/>
<property name="minimum" value="0"/>
<property name="maximum" value="0"/>
<property name="message" value="Line has trailing spaces."/>
</module>
<!-- 每個(gè)java文件一個(gè)語(yǔ)法樹(shù) -->
<module name="TreeWalker">
<!-- 注釋檢查 -->
<!-- 檢查方法和構(gòu)造函數(shù)的javadoc -->
<module name="JavadocMethod">
<property name="tokens" value="METHOD_DEF" />
</module>
<!-- 檢查類和接口的javadoc套么。默認(rèn)不檢查author和version tags -->
<module name="JavadocType"/>
<!-- 檢查變量的javadoc -->
<module name="JavadocVariable"/>
<!-- 檢查javadoc的格式 -->
<module name="JavadocStyle">
<property name="checkFirstSentence" value="false"/>
</module>
<!-- 檢查T(mén)ODO:注釋 -->
<module name="TodoComment"/>
<!-- 命名檢查 -->
<!-- 局部的final變量,包括catch中的參數(shù)的檢查 -->
<module name="LocalFinalVariableName" />
<!-- 局部的非final型的變量碳蛋,包括catch中的參數(shù)的檢查 -->
<module name="LocalVariableName" />
<!-- 包名的檢查(只允許小寫(xiě)字母)胚泌,默認(rèn)^[a-z]+(\.[a-zA-Z_][a-zA-Z_0-9_]*)*$ -->
<module name="PackageName">
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$" />
<message key="name.invalidPattern" value="包名 ''{0}'' 要符合 ''{1}''格式."/>
</module>
<!-- 僅僅是static型的變量(不包括static final型)的檢查 -->
<module name="StaticVariableName" />
<!-- Class或Interface名檢查,默認(rèn)^[A-Z][a-zA-Z0-9]*$-->
<module name="TypeName">
<property name="severity" value="warning"/>
<message key="name.invalidPattern" value="名稱 ''{0}'' 要符合 ''{1}''格式."/>
</module>
<!-- 非static型變量的檢查 -->
<module name="MemberName" />
<!-- 方法名的檢查 -->
<module name="MethodName" />
<!-- 方法的參數(shù)名 -->
<module name="ParameterName " />
<!-- 常量名的檢查(只允許大寫(xiě))肃弟,默認(rèn)^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$ -->
<module name="ConstantName" />
<!-- 定義檢查 -->
<!-- 檢查數(shù)組類型定義的樣式 -->
<module name="ArrayTypeStyle"/>
<!-- 檢查方法名玷室、構(gòu)造函數(shù)、catch塊的參數(shù)是否是final的 -->
<!-- <module name="FinalParameters"/> -->
<!-- 檢查long型定義是否有大寫(xiě)的“L” -->
<module name="UpperEll"/>
<!-- import檢查-->
<!-- 避免使用* -->
<module name="AvoidStarImport"/>
<!-- 檢查是否從非法的包中導(dǎo)入了類 -->
<module name="IllegalImport"/>
<!-- 檢查是否導(dǎo)入了多余的包 -->
<module name="RedundantImport"/>
<!-- 沒(méi)用的import檢查笤受,比如:1.沒(méi)有被用到2.重復(fù)的3.import java.lang的4.import 與該類在同一個(gè)package的 -->
<module name="UnusedImports" />
<!-- 長(zhǎng)度檢查 -->
<!-- 方法不超過(guò)150行 -->
<module name="MethodLength">
<property name="tokens" value="METHOD_DEF" />
<property name="max" value="150" />
</module>
<!-- 方法的參數(shù)個(gè)數(shù)不超過(guò)5個(gè)穷缤。 并且不對(duì)構(gòu)造方法進(jìn)行檢查-->
<module name="ParameterNumber">
<property name="max" value="10" />
<property name="ignoreOverriddenMethods" value="true"/>
<property name="tokens" value="METHOD_DEF" />
</module>
<!-- 空格檢查-->
<!-- 方法名后跟左圓括號(hào)"(" -->
<module name="MethodParamPad" />
<!-- 在類型轉(zhuǎn)換時(shí),不允許左圓括號(hào)右邊有空格箩兽,也不允許與右圓括號(hào)左邊有空格 -->
<module name="TypecastParenPad" />
<!-- Iterator -->
<!-- <module name="EmptyForIteratorPad"/> -->
<!-- 檢查尖括號(hào) -->
<!-- <module name="GenericWhitespace"/> -->
<!-- 檢查在某個(gè)特定關(guān)鍵字之后應(yīng)保留空格 -->
<module name="NoWhitespaceAfter"/>
<!-- 檢查在某個(gè)特定關(guān)鍵字之前應(yīng)保留空格 -->
<module name="NoWhitespaceBefore"/>
<!-- 操作符換行策略檢查 -->
<module name="OperatorWrap"/>
<!-- 圓括號(hào)空白 -->
<module name="ParenPad"/>
<!-- 檢查分隔符是否在空白之后 -->
<module name="WhitespaceAfter"/>
<!-- 檢查分隔符周圍是否有空白 -->
<module name="WhitespaceAround"/>
<!-- 修飾符檢查 -->
<!-- 檢查修飾符的順序是否遵照java語(yǔ)言規(guī)范津肛,默認(rèn)public、protected比肄、private快耿、abstract囊陡、static、final掀亥、transient撞反、volatile、synchronized搪花、native遏片、strictfp -->
<module name="ModifierOrder"/>
<!-- 檢查接口和annotation中是否有多余修飾符,如接口方法不必使用public -->
<module name="RedundantModifier"/>
<!-- 代碼塊檢查 -->
<!-- 檢查是否有嵌套代碼塊 -->
<module name="AvoidNestedBlocks"/>
<!-- 檢查是否有空代碼塊 -->
<module name="EmptyBlock"/>
<!-- 檢查左大括號(hào)位置 -->
<module name="LeftCurly"/>
<!-- 檢查代碼塊是否缺失{} -->
<module name="NeedBraces"/>
<!-- 檢查右大括號(hào)位置 -->
<module name="RightCurly"/>
<!-- 代碼檢查 -->
<!-- 檢查是否在同一行初始化 -->
<!-- <module name="AvoidInlineConditionals"/> -->
<!-- 檢查空的代碼段 -->
<module name="EmptyStatement"/>
<!-- 檢查在重寫(xiě)了equals方法后是否重寫(xiě)了hashCode方法 -->
<module name="EqualsHashCode"/>
<!-- 檢查局部變量或參數(shù)是否隱藏了類中的變量 -->
<module name="HiddenField">
<property name="tokens" value="VARIABLE_DEF"/>
</module>
<!-- 檢查是否使用工廠方法實(shí)例化 -->
<module name="IllegalInstantiation"/>
<!-- 檢查子表達(dá)式中是否有賦值操作 -->
<module name="InnerAssignment"/>
<!-- 檢查是否有"魔術(shù)"數(shù)字 -->
<module name="MagicNumber">
<property name="ignoreNumbers" value="0, 1"/>
<property name="ignoreAnnotation" value="true"/>
</module>
<!-- 檢查switch語(yǔ)句是否有default -->
<module name="MissingSwitchDefault"/>
<!-- 檢查是否有過(guò)度復(fù)雜的布爾表達(dá)式 -->
<module name="SimplifyBooleanExpression"/>
<!-- 檢查是否有過(guò)于復(fù)雜的布爾返回代碼段 -->
<module name="SimplifyBooleanReturn"/>
<!-- 類設(shè)計(jì)檢查 -->
<!-- 檢查類是否為擴(kuò)展設(shè)計(jì)l -->
<!-- <module name="DesignForExtension"/> -->
<!-- 檢查只有private構(gòu)造函數(shù)的類是否聲明為final -->
<module name="FinalClass"/>
<!-- 檢查工具類是否有putblic的構(gòu)造器 -->
<module name="HideUtilityClassConstructor"/>
<!-- 檢查接口是否僅定義類型 -->
<module name="InterfaceIsType"/>
<!-- 檢查類成員的可見(jiàn)度 -->
<module name="VisibilityModifier"/>
</module>
</module>
步驟:首先在本地新建一個(gè)XML文件撮竿,將上面的代碼保存到XML文件中吮便,打開(kāi) settings->Other Settings->CheckStyle,如下圖幢踏,點(diǎn)擊+
在彈出的小窗口中選擇我們剛才保存到本地的配置文件
點(diǎn)擊Next后點(diǎn)擊Finsh髓需,完成配置。
第三步房蝉,測(cè)試我們配置的CheckStyle是否生效
首先僚匆,選擇要測(cè)試的代碼文件,然后右擊選擇Check Current File搭幻,
然后選擇我們配置的規(guī)則咧擂,點(diǎn)擊之后,我們可以看到檀蹋,CheckStyle幫我們指出了代碼中的不規(guī)范松申。
3.2 FindBugs
3.2.1 簡(jiǎn)介
FindBugs 是由馬里蘭大學(xué)提供的一款開(kāi)源 Java 靜態(tài)代碼分析工具。FindBugs 通過(guò)檢查類文件或 JAR 文件俯逾,將字節(jié)碼與一組缺陷模式進(jìn)行對(duì)比從而發(fā)現(xiàn)代碼缺陷贸桶,完成靜態(tài)代碼分析。FindBugs 既提供可視化 UI 界面纱昧,同時(shí)也可以作為 IntelliJ IDEA插件使用刨啸。文本將主要使用將 FindBugs-IDEA 作為 IntelliJ IDEA插件堡赔。
3.2.2 內(nèi)置編程規(guī)范
Bad practice 壞的實(shí)踐:常見(jiàn)代碼錯(cuò)誤识脆,用于靜態(tài)代碼檢查時(shí)進(jìn)行缺陷模式匹配
Correctness 可能導(dǎo)致錯(cuò)誤的代碼,如空指針引用等
國(guó)際化相關(guān)問(wèn)題:如錯(cuò)誤的字符串轉(zhuǎn)換
可能受到的惡意攻擊善已,如訪問(wèn)權(quán)限修飾符的定義等
多線程的正確性:如多線程編程時(shí)常見(jiàn)的同步灼捂,線程調(diào)度問(wèn)題。
運(yùn)行時(shí)性能問(wèn)題:如由變量定義换团,方法調(diào)用導(dǎo)致的代碼低效問(wèn)題悉稠。
3.2.3 在idea中集成FindBugs
File->Setting->Plugins至下圖界面,搜索FindBugs-IDEA艘包,點(diǎn)擊安裝的猛。
3.2.4 在idea中使用FindBugs
安裝好重啟耀盗,在IEDA左下角會(huì)有[圖片上傳失敗...(image-9a21eb-1586735691475)]
標(biāo)致的控制面板,點(diǎn)擊面板卦尊,選擇要分析的Java文件點(diǎn)擊[圖片上傳失敗...(image-5d08d-1586735691475)]
分析叛拷,檢查結(jié)果如下。
插件面板按鈕說(shuō)明
1 分析選中的 Java 文件
2 分析在光標(biāo)所在的類
3 分析選中的包
4 分析選中的模塊 (點(diǎn)擊時(shí)會(huì)詢問(wèn)是否同時(shí)分析 test 包中的類)
5 分析整個(gè)項(xiàng)目 (點(diǎn)擊時(shí)會(huì)詢問(wèn)是否同時(shí)分析 test 包中的類)
6 自定義分析的類
7 分析被修改的類 (搭配 SVN,Git 使用)
8 分析 changelist 中的類 (搭配 SVN,Git 使用)
9 停止分析
10 根據(jù) BUG 類型分組
11 根據(jù)類分組
12 根據(jù)包分組
13 根據(jù) BUG 嚴(yán)重級(jí)別分組
FindBugs 只是一款靜態(tài)代碼分析工具, 雖然分析大多數(shù)的問(wèn)題, 但是如果希望編寫(xiě)更為健壯的程序, 還需進(jìn)行更多的測(cè)試操作, 切不可認(rèn)為 FindBugs 沒(méi)有分析出問(wèn)題便認(rèn)為沒(méi)有問(wèn)題了岂却。
3.3 PMD
3.3.1 簡(jiǎn)介
Facebook 的 Infer 是一個(gè)靜態(tài)分析工具忿薇。Infer 可以分析 Objective-C, Java 或者 C 代碼躏哩,報(bào)告潛在的問(wèn)題署浩。任何人都可以使用 Infer 檢測(cè)應(yīng)用,這可以將那些嚴(yán)重的 bug 扼殺在發(fā)布之前扫尺,同時(shí)防止應(yīng)用崩潰和性能低下筋栋。
3.3.2 內(nèi)置編程規(guī)范
可能的 Bugs:檢查潛在代碼錯(cuò)誤,如空 try/catch/finally/switch 語(yǔ)句
未使用代碼(Dead code):檢查未使用的變量正驻,參數(shù)二汛,方法
復(fù)雜的表達(dá)式:檢查不必要的 if 語(yǔ)句,可被 while 替代的 for 循環(huán)
重復(fù)的代碼:檢查重復(fù)的代碼
循環(huán)體創(chuàng)建新對(duì)象:檢查在循環(huán)體內(nèi)實(shí)例化新對(duì)象
資源關(guān)閉:檢查 Connect拨拓,Result肴颊,Statement 等資源使用之后是否被關(guān)閉掉
3.3.3 在idea中集成PMDPlugin
File->Setting->Plugins至下圖界面,搜索PMDPlugin渣磷,點(diǎn)擊安裝婿着。
3.3.4 在idea中使用PMDPlugin
在代碼編輯框或Project 窗口的文件夾、包醋界、文件右鍵竟宋,選擇“Run PMD”,“Pre Defined”形纺,“All”丘侠,對(duì)指定的文件夾、包逐样、文件進(jìn)行分析:
等待一段時(shí)間蜗字,即可看到分析的結(jié)果。
點(diǎn)擊展開(kāi)即可查看相應(yīng)的具體代碼脂新。
3.4 Jtest
3.4.1 簡(jiǎn)介
Jtest 是 Parasoft 公司推出的一款針對(duì) Java 語(yǔ)言的自動(dòng)化代碼優(yōu)化和測(cè)試工具挪捕,Jtest 的靜態(tài)代碼分析功能能夠按照其內(nèi)置的超過(guò) 800 條的 Java 編碼規(guī)范自動(dòng)檢查并糾正這些隱蔽且難以修復(fù)的編碼錯(cuò)誤。同時(shí)争便,還支持用戶自定義編碼規(guī)則级零,幫助用戶預(yù)防一些特殊用法的錯(cuò)誤。Jtest 提供了基于Eclipse的插件安裝滞乙。Jtest 支持開(kāi)發(fā)人員對(duì) Java 代碼進(jìn)行編碼規(guī)范檢查奏纪,并在 Jtask 窗口中集中顯示檢查結(jié)果鉴嗤。
3.4.2 內(nèi)置編程規(guī)范
可能的錯(cuò)誤:如內(nèi)存破壞、內(nèi)存泄露序调、指針錯(cuò)誤躬窜、庫(kù)錯(cuò)誤、邏輯錯(cuò)誤和算法錯(cuò)誤等
未使用代碼:檢查未使用的變量炕置,參數(shù)荣挨,方法
初始化錯(cuò)誤:內(nèi)存分配錯(cuò)誤、變量初始化錯(cuò)誤朴摊、變量定義沖突
命名約定:檢查命名是否符合命名規(guī)范
Javadoc 注釋:檢查類及方法的 Javadoc 注釋
線程和同步:檢驗(yàn)多線程編程時(shí)常見(jiàn)的同步默垄,線程調(diào)度問(wèn)題
國(guó)際化問(wèn)題:
垃圾回收:檢查變量及 JDBC 資源是否存在內(nèi)存泄露隱患
3.4.3 在idea中集成Jtest
由于本文是基于IntelliJ IDEA講解的,而Jtest目前沒(méi)有基于IntelliJ IDEA的插件甚纲,因此本部分不作描述(自行搜索參考在Eclipse的使用)口锭。
3.4.4 在idea中使用Jtest
由于本文是基于IntelliJ IDEA講解的,而Jtest目前沒(méi)有基于IntelliJ IDEA的插件介杆,因此本部分不作描述(自行搜索參考在Eclipse的使用)鹃操。
4. 錯(cuò)誤檢查能力
為比較上述 Java 靜態(tài)分析工具的代碼缺陷檢測(cè)能力,本文將使用一段示例代碼進(jìn)行試驗(yàn)春哨,示例代碼中將涵蓋我們開(kāi)發(fā)中的幾類常見(jiàn)錯(cuò)誤荆隘,如引用操作、對(duì)象操作赴背、表達(dá)式復(fù)雜化椰拒、數(shù)組使用、未使用變量或代碼段凰荚、資源回收燃观、方法調(diào)用及代碼設(shè)計(jì)幾個(gè)方面。最后本文將分別記錄在默認(rèn)檢查規(guī)范設(shè)置下便瑟,不同工具對(duì)該示例代碼的分析結(jié)果缆毁。以下為示例代碼 Test.java。其中到涂,代碼的注釋部分列舉了代碼中可能存在的缺陷脊框。
4.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;
//缺少空指針判斷
byte[] buffer = new byte[1024];
while ((count = is.read(buffer)) >= 0) {
os.write(buffer, 0, count);
}
//未關(guān)閉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;
//空指針錯(cuò)誤
System.out.println(temp.length());
//未使用變量
int length=a.length;
for(index=0; index&a.length; index++)
{
//多余的if語(yǔ)句
if(true)
{
//對(duì)象比較 應(yīng)使用equals
if(temp==ending)
{
break;
}
//缺少 數(shù)組下標(biāo)越界檢查
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流未關(guān)閉
e.printStackTrace();
}
finally
{
//空的try/catch/finally塊
}
}
}
通過(guò)以上測(cè)試代碼,我們對(duì)已有 Java 靜態(tài)代碼分析工具的檢驗(yàn)結(jié)果做了如下比較养盗。
4.2 Java 靜態(tài)代碼分析工具對(duì)比
由表中可以看出幾種工具對(duì)于代碼檢查各有側(cè)重缚陷。其中适篙,Checkstyle 更偏重于代碼編寫(xiě)格式往核,及是否符合編碼規(guī)范的檢驗(yàn),對(duì)代碼 bug 的發(fā)現(xiàn)功能較弱嚷节;而 FindBugs聂儒,PMD虎锚,Jtest 著重于發(fā)現(xiàn)代碼缺陷。在對(duì)代碼缺陷檢查中衩婚,這三種工具在針對(duì)的代碼缺陷類別也各有不同窜护,且類別之間有重疊。
5.結(jié)論
本文分別從功能非春、特性和內(nèi)置編程規(guī)范等方面詳細(xì)介紹了包括 Checkstyle柱徙,F(xiàn)indBugs,PMD奇昙,Jtest 在內(nèi)的四種主流 Java 靜態(tài)代碼分析工具护侮,并通過(guò)一段 Java 代碼示例對(duì)這四種工具的代碼分析能力進(jìn)行比較。由于這四種工具內(nèi)置編程規(guī)范各有不同储耐,因此它們對(duì)不同種類的代碼問(wèn)題的發(fā)現(xiàn)能力也有所不同羊初。其中 Checkstyle 更加偏重于代碼編寫(xiě)格式檢查,而 FindBugs什湘,PMD长赞,Jtest 著重于發(fā)現(xiàn)代碼缺陷。最后闽撤,希望本文能夠幫助 Java 軟件開(kāi)發(fā)和測(cè)試人員進(jìn)一步了解以上五種主流 Java 靜態(tài)分析工具得哆,并幫助他們根據(jù)需求選擇合適的工具。