參考文獻:https://blog.csdn.net/yang1982_0907/article/details/18086693?utm_source=blogxgwz1
https://blog.csdn.net/liwenxia626/article/details/54932965?utm_source=blogxgwz0
本文主要介紹CheckStyle 5.6.1版本的各個規則配置項目,這個版本的CheckStyle將樣式規則分為了以下十七種類型:
1. Annotations(注解:5個)
- Annotation Use Style(注解使用風格)
這項檢查可以控制要使用的注解的樣式。
- Missing Deprecated(缺少deprecad)
檢查java.lang.Deprecated注解或@deprecated的Javadoc標記是否同時存在。
- Missing Override(缺少override)
當出現{@inheritDoc}的Javadoc標簽時,驗證java.lang.Override注解是否出現。
- Package Annotation(包注解)
這項檢查可以確保所有包的注解都在package-info.java文件中。
- Suppress Warnings(抑制警告)
這項檢查允許你指定不允許SuppressWarnings抑制哪些警告信息。你還可以指定一個TokenTypes列表,其中包含了所有不能被抑制的警告信息。
2. Javadoc Comments(Javadoc注釋:6個)
- Package Javadoc(包注釋)
檢查每個Java包是否都有Javadoc注釋。默認情況下,它只允許使用一個package-info.java文件,但是可以將其配置為允許使用package.html文件。
如果兩個文件都存在的話,就會報告錯誤,因為Javadoc工具不允許這種情況發生。
- Method Javadoc(方法注釋)
<module name="JavadocMethod"> <property name="scope" value="private"/> <property name="allowMissingParamTags" value="false"/> <property name="allowMissingThrowsTags" value="false"/> <property name="allowMissingReturnTag" value="false"/> <property name="tokens" value="METHOD_DEF"/> <property name="allowUndeclaredRTE" value="true"/> <property name="allowThrowsTagsForSubclasses" value="true"/>
</module>
scope: 可以檢查的方法的范圍,例如:public只能檢查public修飾的方法,private可以檢查所有的方法
allowMissingParamTags: 是否忽略對參數注釋的檢查
allowMissingThrowsTags: 是否忽略對throws注釋的檢查
allowMissingReturnTag: 是否忽略對return注釋的檢查
檢查方法或構造器的Javadoc。默認不會檢查未使用的throws。想要將未聲明的java.lang.RuntimeExceptions也納入文檔,則需要將allowUndeclaredRTE屬性設置為true。想要驗證的可見范圍是由scope屬性指定的,默認值為Scope.PRIVATE。想要驗證其他的可見范圍,則需要將scope屬性設置為相應的值。
如果不想顯示由於參數或類型參數沒有在注釋中使用param標記進行說明而產生的錯誤消息,就需要勾選allowMissingParamTags屬性。如果不想顯示由於聲明了拋出異常但沒有在注釋中使用throws標記進行說明而產生的錯誤消息,就需要勾選allowMissingThrowsTags屬性。如果不想顯示由於return語句是非void類型但沒有在注釋中使用return標記進行說明而產生的錯誤消息,就需要勾選allowMissingReturnTag屬性。
由@Override注解標記的方法,它的Javadoc不是必需的。但是在Java 5下,無法為接口中的方法加上標記(已經在Java 6中修正)。因此,CheckStyle支持使用單個的{@inheritDoc}標記來代替所有其他的標記。例如,如果下面的方法實現了接口中的某個方法,那么Javadoc可以這樣編寫:
/** {@inheritDoc} */
public int checkReturnTag(final int aTagIndex,
JavadocTag[] aTags,
int aLineNo)
- Style Javadoc(風格注釋)
<module name="JavadocStyle"> <property name="scope" value="public"/> <property name="checkFirstSentence" value="true"/> <property name="checkEmptyJavadoc" value="true"/> <property name="checkHtml" value="true"/>
</module>
驗證Javadoc注釋,以便於確保它們的格式。可以檢查以下注釋:接口聲明、類聲明、方法聲明、構造器聲明、變量聲明。
- Type Javadoc(類型注釋)
<module name="JavadocType"> <property name="scope" value="protected"/> <property name="scope" value="protected"/> <property name="versionFormat" value="\$Revision.*\$"/> <property name="excludeScope" value="public"/> <property name="tokens" value="CLASS_DEF,INTERFACE_DEF"/> </module>
authorFormat: 檢查author標簽的格式
versionFormat: 檢查version標簽的格式
scope: 可以檢查的類的范圍,例如:public只能檢查public修飾的類,private可以檢查所有的類
excludeScope: 不能檢查的類的范圍,例如:public,public的類將不被檢查,但訪問權限小於public的類仍然會檢查,其他的權限以此類推
tokens: 該屬性適用的類型,例如:CLASS_DEF,INTERFACE_DEF
檢查方法或構造器的Javadoc。默認不會檢查author和version標記。想要驗證的可見范圍是由scope屬性指定的,默認值為Scope.PRIVATE。想要驗證其他的可見范圍,則需要將scope屬性設置為相應的值。想要為author標記或version標記定義格式,將authorFormat屬性或versionFormat屬性設置為指定的正則表達式即可。
如果不想顯示由於類型參數沒有在注釋中使用param標記進行說明而產生的錯誤消息,就需要勾選allowMissingParamTags屬性。
- Variable Javadoc(變量注釋)
<module name="JavadocVariable"> <property name="scope" value="protected"/> </module>
scope: 檢查變量的范圍,例如:public只能檢查public修飾的變量,private可以檢查所有的變量
檢查變量是否具有Javadoc注釋。
- Write Tag(輸出標記)
將Javadoc作為信息輸出。可以作為樣式表來使用,根據作者名排序報告。想要為一個標記定義格式,將tagFormat屬性設置為相應的正則表達式即可。這項檢查會使用兩種不同的嚴重級別。當標記缺失時,使用標准的嚴重級別(Severity)。當標記存在時,使用附加的嚴重級別(tagSeverity)作為報告等級。
3. Naming Conventions(命名規約:12個)
- Abstract Class Name(抽象類名稱)
<module name="AbstractClassName"> <property name="format" value="^Abstract.*$|^.*Factory$"/> </module>
format: 定義抽象類的命名規則
檢查抽象類的名稱是否遵守命名規約。
- Class Type Parameter Name(類的類型參數名稱)
檢查類的類型參數名稱是否遵守命名規約。
- Constant Names(常量名稱)
<module name="ConstantName"> <property name="format" value="^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"/> </module>
format: 定義全局常量的命名規則
檢查常量(用static final修飾的字段)的名稱是否遵守命名規約。
- Local Final Variable Names(局部final變量名稱)
<module name="LocalFinalVariableName"> <property name="format" value="^[A_Z][A-Z0-9]*(_[A_Z0-9]+)*$"/> </module>
format: 定義局部常量的命名規則
檢查局部final變量的名稱是否遵守命名規約。
- Local Variable Names(局部變量名稱)
<module name="LocalVariableName"> <property name="format" value="^[a-z][a-zA-Z0-9]*$"/> </module>
format: 定義局部變量的命名規則
檢查局部變量的名稱是否遵守命名規約。
- Member Names(成員名稱)
<module name="MemberName"> <property name="format" value="^[a-z][a-zA-Z0-9]*$"/> <property name="applyToPublic" value="true"/> <property name="applyToProtected" value="true"/> <property name="applyToPackage" value="true"/> <property name="applyToPrivate" value="true"/> </module>
format: 定義非靜態成員變量的命名規則
applyToPublic: 是否適用於public的成員變量
applyToProtected: 是否適用於protected的成員變量
applyToPackage: 是否適用於package的成員變量
applyToPrivate: 是否適用於private的成員變量
檢查成員變量(非靜態字段)的名稱是否遵守命名規約。
- Method Names(方法名稱)
<module name="MethodName"> <property name="format" value="^[a-z][a-zA-Z0-9]*$"/> </module>
format: 定義方法名的命名規則
檢查方法名稱是否遵守命名規約。
- Method Type Parameter Name(方法的類型參數名稱)
檢查方法的類型參數名稱是否遵守命名規約。
- Package Names(包名稱)
<module name="PackageName"> <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/> </module>
format: 定義包名的命名規則
檢查包名稱是否遵守命名規約。
- Parameter Names(參數名稱)
<module name="ParameterName"> <property name="format" value="^[a-z][a-zA-Z0-9]*$"/> </module>
format: 定義參數名的命名規則
檢查參數名稱是否遵守命名規約。
- Static Variable Names(靜態變量名稱)
<module name="StaticVariableName"> <property name="format" value="^[a-z][a-zA-Z0-9]*$"/> </module>
format: 定義靜態變量的命名規則
檢查靜態變量(用static修飾,但沒用final修飾的字段)的名稱是否遵守命名規約。
- Type Names(類型名稱)
<module name="TypeName"> <property name="format" value="^[A-Z][a-zA-Z0-9]*$"/> <property name="tokens" value="CLASS_DEF,INTERFACE_DEF"/> </module>
format: 定義類和接口的命名規則
tokens: 定義規則適用的類型,例如:CLASS_DEF表示類,INTERFACE_DEF 表示接口
檢查類的名稱是否遵守命名規約。
4. Headers(文件頭:2個)
- Header(文件頭)
檢查源碼文件是否開始於一個指定的文件頭。headerFile屬性可以指定一個文件,該文件包含了需要的文件頭。還有另一種方法,文件頭的內容可以直接在header屬性中設置,而不使用外部文件。
ignoreLines屬性可以設置行號,指定檢查時忽略頭文件中的哪些行。如果想要支持包含版權日期的文件頭,那么這個屬性就顯得非常有用。例如,考慮下面的文件頭:
line 1: ////////////////////////////////////////////////////////////////////
line 2: // checkstyle:
line 3: // Checks Java source code for adherence to a set of rules.
line 4: // Copyright (C) 2002 Oliver Burn
line 5: ////////////////////////////////////////////////////////////////////
因為年份信息會隨着時間改變,通過將ignoreLines屬性設置為4,你就可以告訴CheckStyle忽略第4行。
- Regular Expression Header(正則表達式文件頭)
檢查Java源碼文件頭部的每行是否匹配指定的正則表達式。
解釋:在某些項目中,檢查固定的頭部是不足夠的,例如,文件頭可能需要一行版權信息,但是其中的年份信息會隨着時間變化。
例如,考慮下面的文件頭:
line 1: ^/{71}$
line 2: ^// checkstyle:$
line 3: ^// Checks Java source code for adherence to a set of rules\.$
line 4: ^// Copyright CC \d\d\d\d Oliver Burn$
line 5: ^// Last modification by \$Author.*\$$
line 6: ^/{71}$
line 7:
line 8: ^package
line 9:
line 10: ^import
line 11:
line 12: ^/\*\*
line 13: ^ \*([^/]|$)
line 14: ^ \*/
第1行和第6行說明了如何更緊湊地表示71個'/'符號。第4行表示版權信息中的年份的格式是四位數字。第5行示范了如何在文件頭部添加校正控制關鍵字。第12-14行是Javadoc的模板(第13行非常復雜,它可以抑制Javadoc注釋中的沖突)。
5. Imports(導入:7個)
- Avoid Star (Demand) Imports(避免通配符導入)
<module name="AvoidStarImport"> <property name="excludes" value="java.io,java.util"/> </module>
excludes: 定義可以使用*導入的包
檢查沒有import語句使用*符號。
解釋:從一個包中導入所有的類會導致包之間的緊耦合,當一個新版本的庫引入了命名沖突時,這樣就有可能導致問題發生。
- Avoid Static Imports(避免靜態導入)
檢查沒有靜態導入語句。
解釋:導入靜態成員可能會導致類成員之間的命名沖突。導入靜態成員可能會導致代碼的可讀性很差,因為讀者可能會搞不清楚成員到底位於哪個類中。
- Illegal Imports(非法導入)
<module name="IllegalImport"/>
illegalPkgs: 定義非法的包名稱
檢查是否導入了指定的非法包。默認情況下,這項檢查會拒絕所有的sun.*包,因為直接使用sun.*包的程序肯定不是100%的純Java程序。想要拒絕其他的包,將illegalPkgs屬性設置為你指定的非法包列表即可。
- Import Order Check(導入順序檢查)
<module name="ImportOrder"> <property name="groups" value="java,javax"/> <property name="ordered" value="true"/> <property name="separated" value="true"/> <property name="caseSensitive" value="true"/> </module>
groups: 定義導入包的順序,默認以字母順序導入
ordered: 定義包是否必須按規定的順序顯示
separated: 定義包與包之間是否應添加空白行
caseSensitive: 是否區分包名的大小寫
檢查導入包的順序/分組。確保導入包的分組按照指定的順序排列(例如,java.排在首位,javax.排在第二,以此類推),並且每個分組內導入的包都是按照字典序排列的。靜態導入必須放在最后,並且也是按照字典序排列的。
- Redundant Imports(多余導入)
<module name="RedundantImport"/>
檢查是否存在多余的導入語句。如果一條導入語句滿足以下條件,那么就是多余的:
1. 它是另一條導入語句的重復。也就是,一個類被導入了多次。
2. 從java.lang包中導入類,例如,導入java.lang.String。
3. 從當前包中導入類。
- Unused Imports(未使用導入)
<module name="UnusedImports"/>
檢查未使用的導入語句。CheckStyle使用一種簡單可靠的算法來報告未使用的導入語句。如果一條導入語句滿足以下條件,那么就是未使用的:
1. 沒有在文件中引用。這種算法不支持通配符導入,例如,java.io.*;。大多數IDE檢查帶有通配符的導入語句時,使用的算法非常復雜。
2. 它是另一條導入語句的重復。也就是,一個類被導入了多次。
3. 從java.lang包中導入類,例如,導入java.lang.String。
4. 從當前包中導入類。
5. 可選:在Javadoc注釋中引用它。這項檢查默認是關閉的,因為僅僅為了抽出文檔而引入了一個編譯時依賴是一個很壞的習慣。例如,當Javadoc注釋中包含{@link Date}時,就會認為import java.util.Date被引用了。想要避免引入編譯時依賴,可把Javadoc注釋寫成{@link java.util.Date}
- Import Control(導入控制)
控制允許導入每個包中的哪些類。可用於確保應用程序的分層規則不會違法,特別是在大型項目中。
導入控制XML文檔所使用的DTD文件位於
http://www.puppycrawl.com/dtds/import_control_1_0.dtd。它包含了上述XML文檔的所有元素和屬性。
6. Size Violations(尺寸超標:8個)
- Anonymous inner classes lengths(匿名內部類長度)
<module name="AnonInnerLength"> <property name="max" value="20"/> </module>
max: 定義匿名內部類最多容許的行數
檢查匿名內部類的長度。
解釋:如果一個匿名內部類變得非常長,那么就很難讀懂它,並且難以跟蹤方法的流程。因此,過長的匿名內部類通常應當被重構為一個具名內部類。可以參考Bloch編寫的《Effective Java》的第93頁。
- Executable Statement Size(可執行語句數量)
<module name="ExecutableStatementCount"> <property name="max" value="20"/> <property name="tokens" value="CTOR_DEF,METHOD_DEF,STATIC_INIT,INSTANCE_INIT"/> </module>
max: 定義所能容許的語句的最多數目
tokens: 定義可以檢查的類型,例如:CTOR_DEF,METHOD_DEF,STATIC_INIT,INSTANCE_INIT
檢查要執行的語句的數目,將可執行語句的數量限制為一個指定的限值。
- Maximum File Length(最大文件長度)
<module name="FileLength"> <property name="max" value="1000"/> </module>
max: 定義一個文件所能容許的行數
檢查源碼文件的長度。
解釋:如果一個源碼文件變得非常長,那么就會難以理解。因此,過長的類通常應當被重構成若干個較小的獨立的類,每個類專注於一個特定的任務。
- Maximum Line Length(最大行長度)
<module name="LineLength"> <property name="max" value="80"/> <property name="ignorePattern" value="^ *\* *[^ ]+$"/> </module>
檢查源碼每行的長度。
解釋:將源碼打印出來,或者開發者限制了屏幕顯示源碼的空間(例如,IDE會顯示額外的信息,諸如項目樹、類層次等等),那么過長的行會顯得難以閱讀。
- Maximum Method Length(最大方法長度)
<module name="MethodLength"> <property name="max" value="100"/> <property name="countEmpty" value="true"/> <property name="tokens" value="METHOD_DEF"/> </module>
max: 最多容許的行數
countEmpty: 是否計算空行
tokens: 定義檢查的類型
檢查方法和構造器的長度。
解釋:如果一個方法變得非常長,那么就會難以理解。因此,過長的方法通常應當被重構成若干個較小的獨立的方法,每個方法專注於一個特定的任務。
- Maximum Parameters(最大參數數量)
<module name="ParameterNumber"> <property name="max" value="10"/> <property name="tokens" value="METHOD_DEF,CTOR_DEF"/> </module>
max: 定義最多有多少個參數
tokens: 定義檢查的類型
檢查一個方法或構造器的參數的數量。
- Outer Type Number(外層類型數量)
檢查在一個文件的外層(或根層)中聲明的類型的數量。
解釋:一般認為給每個文件只定義一個外層類型是較好的做法。
- Method Count(方法總數)
檢查每個類型中聲明的方法的數量。
它包括了每種可見范圍方法的總數(private、package、protected、public)。
7. Whitespace(空格:12個)
- Generic Whitespace(范型標記空格)
檢查范型標記<和>的周圍的空格是否遵守標准規約。這項規約是不可配置的。
例如,以下代碼都是合法的:
List x = new ArrayList();
List> y = new ArrayList>();
但是下面的示例代碼是不合法的:
List < Integer > x = new ArrayList < Integer > ();
List < List < Integer > > y = new ArrayList < List < Integer > > ();
- Empty For Initializer Pad(空白for初始化語句填充符)
<module name="EmptyForInitializerPad"> <property name="option" value="space"/> </module>
option: 定義初始化語句中是否使用空格,例如:space表示使用空格,則for(int i = 0; i < 100; i++)就是符合格式要求的,而for(int i=0; i<100;i++)就不符合要求
檢查空的for循環初始化語句的填充符,也就是空格是否可以作為for循環初始化語句空位置的填充符。如果代碼自動換行,則不會進行檢查,正如以下代碼所示:
for (
; i < j; i++, j--)
- Empty For Iterator Pad(空白for迭代器填充符)
<module name="EmptyForIteratorPad"> <property name="option" value="space"/> </module>
檢查for iterator語句是否使用空格
option:定義初始化語句是否使用空格,例如:space表示使用空格,則for(Iterator iterator = List.iterator();iterator.hasNext(); iterator.next())就是形式合理的,否則就是形式不合理的
檢查空的for循環迭代器的填充符,也就是空格是否可以作為for循環迭代器空位置的填充符。如果代碼自動換行,則不會進行檢查,正如以下代碼所示:
for (Iterator foo = very.long.line.iterator();
foo.hasNext();
)
- No Whitespace After(指定標記之后沒有空格)
<module name="NoWhitespaceAfter"/>
檢查指定標記之后沒有空格。若要禁用指定標記之后的換行符,將allowLineBreaks屬性設為false即可。
- No Whitespace Before(指定標記之前沒有空格)
<module name="NoWhitespaceBefore"/>
檢查指定標記之前沒有空格。若要允許指定標記之前的換行符,將allowLineBreaks屬性設為true即可。
- Operator Wrap(運算符換行)
<module name="OperatorWrap"> <property name="tokens" value="ASSIGN, DIV, DIV_ASSIGN, PLUS_ASSIGN, MINUS, MINUS_ASSIGN, STAR, STAR_ASSIGN, MOD, MOD_ASSIGN, SR, SR_ASSIGN, BSR, BSR_ASSIGN, SL, SL_ASSIGN, BXOR, BXOR_ASSIGN, BOR, BOR_ASSIGN, BAND, BAND_ASSIGN,PLUS, QUESTION"/> <property name="option" value="eol"/> </module>
option: 定義運算符的位置,eol在同一行,nl在下一行
tokens: 定義檢查的類型
檢查代碼自動換行時,運算符所處位置的策略。nl表示運算符必須在新行中,eol表示運算符必須在當前行的行末。
- Method Parameter Pad(方法參數填充符)
<module name="MethodParamPad"> <property name="allowLineBreaks" value="false"/> <property name="option" value="space"/> <property name="tokens" value="METHOD_DEF,CTOR_DEF"/> </module>
allowLineBreaks: 參數是否允許在不同行
option: 在參數和括號、參數和標識符之間是否包含空格
tokens: 檢查的類型
檢查方法定義、構造器定義、方法調用、構造器調用的標識符和參數列表的左圓括號之間的填充符。也就是,如果標識符和左圓括號位於同一行,那么就檢查標識符之后是否需要緊跟一個空格。如果標識符和左圓括號不在同一行,那么就報錯,除非將規則配置為允許使用換行符。想要在標識符之后使用換行符,將allowLineBreaks屬性設置為true即可。
- Paren Pad(圓括號填充符)
<module name="ParenPad"> <property name="option" value="nospace"/> </module>
檢查左小括號'('后邊和右小括號')'前邊是否有空格
option: space表示有空格,nospace表示沒有空格
tokens: 定義檢查的類型
檢查圓括號的填充符策略,也就是在左圓括號之后和右圓括號之前是否需要有一個空格。
- Typecast Paren Pad(類型轉換圓括號填充符)
<module name="TypecastParenPad"> <property name="option" value="space"/> </module>
檢查類型轉換的圓括號的填充符策略。也就是,在左圓括號之后和右圓括號之前是否需要有一個空格。
- File Tab Character(文件制表符)
<module name="TabCharacter"/>
檢查源碼中沒有制表符('\t')。
解釋:
1. 為了能夠更方便地閱讀源碼,開發者不應當在他們的文本編輯器中配置制表符的寬度。
2. 根據Apache Jakarta的編碼標准:在一個分布式開發環境中,當提交的消息被發送到一個郵件列表中時,如果你使用了制表符,那么這些消息會變得幾乎不可能閱讀。
- Whitespace After(指定標記之后有空格)
<module name="WhitespaceAfter"> <property name="tokens" value="COMMA,SEMI,TYPECAST"/> </module>
tokens: 檢查的類型
檢查指定標記之后是否緊跟了空格。
- Whitespace Around(指定標記周圍有空格)
<module name="WhitespaceAround"> <property name="tokens" value="ASSIGN"/> </module>
檢查指定標記的周圍是否有空格。以下形式的空構造器和方法的代碼體(代碼塊):
public MyClass() {} // 空構造器
public void func() {} // 空方法
可以選擇性地從檢查策略中排除,通過設置allowEmptyMethods和allowEmptyConstructors屬性即可。
8. Regexp(正則表達式:3個)
- RegexpSingleline(正則表達式單行匹配)
檢查單行是否匹配一條給定的正則表達式。可以處理任何文件類型。
解釋:這項檢查可以作為原型檢查使用,能夠發現常見的編碼壞習慣,例如調用ex.printStacktrace()、System.out.println()、System.exit(),等等。
- RegexpMultiline(正則表達式多行匹配)
檢查多行是否匹配一條給定的正則表達式。可以處理任何文件類型。
解釋:這項檢查可以作為原型檢查使用,能夠發現常見的編碼壞習慣,例如調用ex.printStacktrace()、System.out.println()、System.exit(),等等。
- RegexpSingleLineJava(正則表達式單行Java匹配)
這項檢查是RegexpSingleline的變種,用於檢測Java文件中的單行是否匹配給定的正則表達式。它支持通過Java注釋抑制匹配操作。
9. Modifiers(修飾符:2個)
- Modifier Order(修飾符順序)
<module name="ModifierOrder"/>
檢查代碼中的標識符的順序是否符合《Java Language Specification》中的第8.1.1、8.3.1章節所建議的順序。正確的順序應當如下:
1. public
2. protected
3. private
4. abstract
5. static
6. final
7. transient
8. volatile
9. synchronized
10. native
11. strictfp
- Redundant Modifier(多余修飾符)
<module name="RedundantModifier"> <property name="tokens" value="METHOD_DEF,VARIABLE_DEF"/> </module>
tokens: 檢查的類型
在以下部分檢查是否有多余的修飾符:
1. 接口和注解的定義;
2. final類的方法的final修飾符;
3. 被聲明為static的內部接口聲明。
解釋:《Java Language Specification》強烈不建議在接口定義中使用“public”和“abstract”來聲明方法。
接口中的變量和注解默認就是public、static、final的,因此,這些修飾符也是多余的。
因為注解是接口的一種形式,所以它們的字段默認也是public、static、final的,正如它們的注解字段默認是public和abstract的。
定義為final的類是不能被繼承的,因此,final類的方法的final修飾符也是多余的。
10. Blocks(代碼塊:5個)
- Avoid Nested Blocks(避免嵌套代碼塊)
<module name="AvoidNestedBlocks"> <property name="allowInSwitchCase" value="true"/> </module>
allowInSwitchCase: 定義是否允許switch case中使用嵌套的代碼塊
找到嵌套代碼塊,也就是在代碼中無節制使用的代碼塊。
解釋:內嵌代碼塊通常是調試過程的殘留物,它們會使讀者產生混淆。
- Empty Block(空代碼塊)
<module name="EmptyBlock"> <property name="option" value="stmt"/> </module>
option: 定義代碼塊中應該包含的內容,例如:stmt表示語句
tokens: 檢查的類型
檢查空代碼塊。
- Left Curly Brace Placement(左花括號位置)
<module name="LeftCurly"> <property name="option" value="eol"/> <property name="maxLineLength" value="80"/> <property name="tokens" value="CLASS_DEF,INTERFACE_DEF,METHOD_DEF,CTOR_DEF"/> </module>
option: 定義左大括號'{'顯示位置,eol在同一行顯示,nl在下一行顯示
maxLineLength: 大括號'{'所在行行最多容納的字符數
tokens: 該屬性適用的類型,例:CLASS_DEF,INTERFACE_DEF,METHOD_DEF,CTOR_DEF
檢查代碼塊的左花括號的放置位置。
通過property選項指定驗證策略。
若使用eol和nlow策略,則需要考慮maxLineLength屬性。
- Need Braces(需要花括號)
<module name="NeedBraces"/>
檢查是否應該使用括號的地方沒有加括號
tokens: 定義檢查的類型
檢查代碼塊周圍是否有大括號,可以檢查do、else、if、for、while等關鍵字所控制的代碼塊。
- Right Curly Brace Placement(右花括號位置)
<module name="RightCurly"> <property name="option" value="alone"/> <property name="tokens" value="LITERAL_TRY"/> </module>
option: 右大括號是否單獨一行顯示
tokens: 定義檢查的類型
檢查else、try、catch標記的代碼塊的右花括號的放置位置。
通過property選項指定驗證策略。
11. Coding Problems(編碼問題:43個)
- Avoid Inline Conditionals(避免內聯條件語句)
<module name="AvoidInlineConditionals"/>
檢查是否在同一行初始化, 例如:private int Age = nGe==1 ? 100 : 0; 就應該避免
檢測內聯條件語句。內聯條件語句的一個示例如下所示:
String a = getParameter("a");
String b = (a==null || a.length<1) ? null : a.substring(1);
解釋:有些開發者發現內聯條件語句很難讀懂,因此他們公司的編碼標准會禁止使用內聯條件語句。
- Covariant Equals(共變equals方法)
<module name="CovariantEquals"/>
檢查定義了共變equals()方法的類中是否同樣覆蓋了equals(java.lang.Object)方法。這項檢查受到FindBugs的啟發。
解釋:錯誤地定義了一個共變equals()方法,而沒有覆蓋equals(java.lang.Object)方法,可能會產生不可預料的運行時行為。
- Default Comes Last(默認分支置於最后)
<module name="DefaultComesLast"/>
檢查switch語句中的default是否在所有的case分支之后。
解釋:Java允許default位於switch語句中的任何地方。但是,如果default位於最后一個case分支之后,那么代碼的可讀性會更強。
- Declaration Order Check(聲明順序檢查)
根據Java編程語言的編碼規約,一個類或接口的聲明部分應當按照以下順序出現:
1. 類(靜態)變量。首先應當是public類變量,然后是protected類變量,然后是package類變量(沒有訪問標識符),最后是private類變量。
2. 實例變量。首先應當是public類變量,然后是protected類變量,然后是package類變量(沒有訪問標識符),最后是private類變量。
3. 構造器
4. 方法
- Empty Statement(空語句)
檢測代碼中是否有空語句(也就是單獨的;符號)。
- Equals Avoid Null(避免調用空引用的equals方法)
檢查equals()比較方法中,任意組合的String常量是否位於左邊。
這項檢查還會處理String.equalsIgnoreCase()調用(可以抑制這種警告)。
解釋:調用String常量的equals()方法可以避免潛在的NullPointerException。同樣,經常會發現在調用equals()方法之前,會進行空指針檢查,不過在下面的示例中則沒有必要這么做。
例如:
String nullString = null;
nullString.equals("My_Sweet_String");
這段代碼應當重構為:
String nullString = null;
"My_Sweet_String".equals(nullString);
局限:如果覆蓋了equals方法,或者定義了一個共變equals方法,並且沒有正確地實現這個方法(也就是s.equals(t)返回的結果和t.equals(s)返回的結果不同),那么改寫調用方法的對象和參數可能會產生無法預料的結果。
Java的Autoboxing特性會對這項檢查如何實現產生影響。在Java 5之前的版本,所有的IDENT + IDENT對象拼接不會導致NullPointerException,即使它是空指針。這項檢查已經包含了這些情況。它們會進行簡單的處理,就好像使用String.valueof()方法包圍起來一樣,這個方法會拼接null字符串。
以下示例將會導致一個NullPointerException,這是Autoboxing功能所造成的結果:
Integer i = null, j = null;
String number = "5"
number.equals(i + j);
因為很難確定正在拼接的是哪種類型的對象,所以所有的IDENT拼接都會被認為是不安全的。
- Equals and HashCode(equals方法和hashCode方法)
<module name="EqualsHashCode"/>
檢查覆蓋了equals()方法的類是否也覆蓋了hashCode()方法。
解釋:equals()方法和hashCode()方法約定,相等的對象必然具有相同的哈希碼。因此,只要你覆蓋了equals()方法,你就必須同時覆蓋hashCode()方法,以確保可以在基於哈希的集合中使用你的類。
- Explicit Initialization(顯式初始化)
<module name="ExplicitInitialization"/>
檢查類或對象的成員是否顯式地初始化為成員所屬類型的默認值(對象引用的默認值為null,數值和字符類型的默認值為0,布爾類型的默認值為false)。
解釋:每個實例變量都會被初始化兩次,並且初始化為相同的值。在執行代碼中指定的任何初始化操作之前,Java會初始化每個實例變量為它的默認值(0或null)。因此在這種情況下,x會被初始化為0兩次,bar會被初始化為null兩次。因此,這樣稍微有些效率低下。這種編碼風格是C/C++編碼風格的延續,它表明開發者並不是真正有把握Java能夠初始化實例變量為它的默認值。
- Fall Through(跨越分支)
<module name="FallThrough"/>
檢查switch語句中是否存在跨越分支。如果一個case分支的代碼中缺少break、return、throw或continue語句,那么就會導致跨越分支。
這項檢查可以通過特殊的注釋以抑制警告。默認情況下,在有跨越分支的case分支代碼中添加“fallthru”、“fall through”、“fallthrough”、“falls through”、“fallsthrough”等注釋(區分大小寫)時,便可抑制警告。包含以上單詞的注釋必須在一行中,並且必須在當前case分支代碼的最后一行中,或者與case語句在同一行,如以下代碼所示:
switch (i){
case 0:
i++; // fall through
case 1:
i++;
// falls through
case 2: {
i++;
}
// fallthrough
case 3:
i++;
/* fallthru */case 4:
i++
break;
}
注意:這項檢查假設case分支代碼中沒有不可達的代碼。
- Final Local Variable(final局部變量)
<module name="FinalLocalVariable"/>
檢查變量值沒有改動的情況下,該變量是否定義成了final
檢查從未改變取值的局部變量是否被聲明為final。這項檢查還可以被配置為檢查未修改過的參數是否被聲明為final。
當配置為檢查參數時,這項檢查會忽略接口方法和抽象方法中的參數。
- Hidden Field(隱藏字段)
<module name="HiddenField"/>
檢查局部變量或參數是否會遮蔽在相同類中定義的字段。
- Illegal Instantiation(非法實例化)
<module name="IllegalInstantiation"/>
檢查是否有不合法的實例化操作,是否使用工廠方法更好。
解釋:根據不同的項目,對於某些類來說,可能通過工廠方法來創建類實例更好,而不是調用類構造器。
一個簡單的示例就是java.lang.Boolean類。為了節省內存和CPU周期,最好使用預定義的常量TRUE和FALSE。構造器的調用應當被替換為調用Boolean.valueOf()方法。
某些對性能有極端要求的項目可能需要其他的類也使用工廠方法,以便於提高緩存或對象池的使用效率。
- Illegal Catch(非法異常捕捉)
<module name="IllegalCatch"/>
從不允許捕捉java.lang.Exception、java.lang.Error、java.lang.RuntimeException的行為。
解釋:缺乏經驗的開發者經常會簡單地捕捉Exception異常,試圖處理多種異常類型。這會很不幸地使代碼無意中捕捉到NullPointerException、OutOfMemoryErrors等系統異常。
- Illegal Throws(非法異常拋出)
這項檢查可以用來確保類型不能聲明拋出指定的異常類型。從不允許聲明拋出java.lang.Error或java.lang.RuntimeException。
- Illegal Tokens(非法標記)
檢查不合法的標記。
解釋:某個語言特性經常會導致代碼難以維護,或者開發新手難以理解。在某些框架中,其他特性可能不推薦使用,例如,在EJB組件中最好不要使用本地方法。
- Illegal Tokens Text(非法標記文本)
檢查是否有不合法的標記文本。
- Illegal Type(非法類型)
檢查代碼中是否有在變量聲明、返回值、參數中都沒有作為類型使用過的特定類。包括一種格式檢查功能,默認情況下不允許抽象類。
解釋:幫助減少和實體類之間的耦合。另外,抽象類應當被認為是接口的一種簡便的基類實現,因此不能是類型本身。
- Inner Assignment(內部賦值)
檢查子表達式中是否有賦值語句,例如String s = Integer.toString(i = 2);。
解釋:這項檢查會忽略for循環代碼,其余所有的賦值操作都應當在它們自己的頂層語句中,以便於增強可讀性。在上述的內部賦值代碼中,很難看到變量是在哪兒賦值的。
- JUnit Test Case(JUnit測試用例)
確保setUp()、tearDown()方法的名稱正確,沒有任何參數,返回類型為void,是public或protected的。
同樣確保suite()方法的名稱正確,沒有參數,返回類型為junit.framewotk.Test,並且是public和static的。
解釋:開發者時常會錯誤地命名這些方法,並且不會意識到這些方法沒有被調用。
- Magic Number(幻數)
檢查代碼中是否含有“幻數”,幻數就是沒有被定義為常量的數值文字。默認情況下,-1、0、1、2不會被認為是幻數。
- Missing Constructor(缺少構造器)
檢查類(除了抽象類)是否定義了一個構造器,而不是依賴於默認構造器。
- Missing Switch Default(缺少switch默認分支)
<module name="MissingSwitchDefault"/>
檢查switch語句是否含有default子句。
解釋:在每個switch語句中引入一條默認分支通常是一個很好的主意。即使開發者確信所有當前可能的分支都能覆蓋到,這也應當在default分支中表達出來,例如,使用一條斷言。這種方法使得代碼可以應付以后的修改,例如,在一個枚舉類型中引入新的類型。
- Modified Control Variable(修改控制變量)
<module name="ModifiedControlVariable"/>
檢查循環控制變量是否被修改
檢查確保for循環的控制變量沒有在for代碼塊中被修改。示例代碼如下:
for (int i = 0; i < 1; i++) {
i++;
}
解釋:如果在循環體中修改了控制變量,程序流程就會變得更加難以跟蹤。可以用while循環替換for循環。
- Multiple String Literals(多重字符串常量)
<module name="MultipleStringLiterals"> <property name="allowedDuplicates" value="3"/> </module>
檢查一個字符串變量在不改變變量值的情況下或者字符串出現的次數
allowedDuplicates: 定義在類中一個字符串變量在不改變變量值的情況下或者字符串所能使用的最多次數
檢查在單個文件中,相同的字符串常量是否出現了多次。
解釋:重復代碼會使得維護工作變得更加困難,因此最好用一個常量來替換多次出現。
- Multiple Variable Declaration(多重變量聲明)
<module name="MultipleVariableDeclarations"/>
檢查一次聲明多個變量時,變量是否在同一行或者在同一個語句中
檢查每個變量是否使用一行一條語句進行聲明。
解釋:《SUN編碼規約》的第6.1章節推薦應當使用一行一條語句聲明一個變量。
- Nested For Depth(for嵌套深度)
限制for循環的嵌套層數(默認值為1)。
- Nested If Depth(if嵌套深度)
<module name="NestedIfDepth"> <property name="max" value="1"/> </module>
限制if-else代碼塊的嵌套層數(默認值為1)。
- Nested Try Depth(try嵌套深度)
<module name="NestedTryDepth"> <property name="max" value="3"/> </module>
限制try代碼塊的嵌套層數(默認值為1)。
- No Clone(沒有clone方法)
檢查是否覆蓋了Object類中的clone()方法。
解釋:clone()方法依賴於一套奇怪且難以遵循的規則,這套規則並不是在所有情況下都起作用。因此,很難正確地覆蓋clone()方法。下面是一些說明為何應當避免使用clone()方法的原因。
支持clone方法的類應當事先Cloneable接口,但是Cloneable結構並不包含clone方法。因此,它並不會強制覆蓋clone方法。
Cloneable接口會強迫對象的clone方法正確地工作。如果不實現這個接口,那么對象的clone方法會拋出CloneNotSupportedException。沒有用final關鍵字修飾的類必須返回由調用super.clone()方法所返回的對象。用final關鍵字修飾的類可以使用一個構造器創建一個克隆對象,這個對象和非final類的有所不同。
如果一個父類沒有正確地實現clone方法,那么所有的子類調用super.clone()方法時,都會注定失敗。
如果一個類含有可變對象的引用,那么在這個類的clone方法中調用super.clone()方法之后,必須使用前述可變對象的拷貝來替換這些對象引用。
clone方法不能正確地處理用final關鍵字修飾的可變對象引用,因為final引用不能被重新賦值。
如果一個父類覆蓋了clone方法,那么所有的子類都必須提供一個正確的clone實現。
在某些情況下,有兩種clone方法的替代方案可以使用,一種是使用一個拷貝構造器,另一種是使用靜態工廠方法來返回某個對象的拷貝。這兩種方法都更加簡單,並且不會和final關鍵字修飾的字段產生沖突。它們不會強迫調用的客戶端處理CloneNotSupportException。它們都是具有類型的,因此不需要進行任何類型轉換。最后,它們更加靈活,因為它們可以處理接口類型,而不僅僅是實體類。
有時候,不能使用拷貝構造器或靜態工廠作為clone方法的替代方案。以下示例說明了拷貝構造器或靜態工廠的局限性。假設Square是Shape的一個子類。
Shape s1 = new Square();
System.out.println(s1 instanceof Square); //true
...假設此處的代碼不知道s1是一個Square類型的對象,這是多態的優美之處,但是代碼想要拷貝這個被聲明為Shape類型的Square對象,Shape是Square的父類...
Shape s2 = new Shape(s1); //using the copy constructor
System.out.println(s2 instanceof Square); //false
有效的解決辦法(不用知道所有的子類,並且不用執行大量的類型轉換)應當如下列代碼所示(假設實現了正確的clone方法):
Shape s2 = s1.clone();
System.out.println(s2 instanceof Square); //true
你只需要記住,如果需要這種多態克隆的類型,那么一個正確實現的clone方法可能才是最好的選擇。
這項檢查和{@link NoFinalizerCheck}幾乎完全相同。
- No Finalizer(沒有finalize方法)
驗證類中是否定義了finalize()方法。
- Package Declaration(包聲明)
<module name="PackageDeclaration"/>
確保一個類具有一個包聲明,並且(可選地)包名要與源代碼文件所在的目錄名相匹配。
解釋:位於空包中的類是不能夠被導入的。很多開發新手並沒有注意到這一點。
- Parameter Assignment(參數賦值)
<module name="ParameterAssignment"/>
不允許對參數進行賦值。
解釋:對參數的賦值通常被認為是缺乏編程實踐經驗。強迫開發者將參數聲明為final通常是非常麻煩的。這項檢查可以確保參數從不會被賦值,這對於雙方都是好事。
- Redundant Throws(多余的throws)
<module name="RedundantThrows"> <property name="allowUnchecked" value="true"/> <property name="allowSubclasses" value="true"/> </module>
檢查throws子句中是否聲明了多余的異常,例如重復異常、未檢查的異常或一個已聲明拋出的異常的子類。
- Require This(需要this)
<module name="RequireThis"> <property name="checkFields" value="false"/> <property name="checkMethods" value="false"/> </module>
檢查是否使用了this
checkFields: 是否檢查變量引用
checkMethods: 是否檢查方法調用
檢查代碼是否使用了“this.”,也就是說,在默認情況下,引用當前對象的實例變量和方法時,應當顯式地通過“this.varName”或“this.methodName(args)”這種形式進行調用。
- Return Count(return總數)
<module name="ReturnCount"> <property name="max" value="3"/> </module>
限制return語句的數量。默認值為2。可以忽略檢查指定的方法(默認忽略equals()方法)。
解釋:過多的返回點可能表明代碼嘗試處理過多的業務,可能會難以理解。
- Simplify Boolean Expression(簡化布爾表達式)
<module name="SimplifyBooleanExpression"/>
檢查是否有過於復雜的布爾表達式。現在能夠發現諸如if (b == true)、b || true、!false等類型的代碼。
解釋:復雜的布爾邏輯會使得代碼難以理解和維護。
- Simplify Boolean Return(簡化布爾返回值)
<module name="SimplifyBooleanReturn"/>
檢查是否有過於復雜的布爾類型return語句。例如下面的代碼:
if (valid())
return false;
else
return true;
可以寫成:
return !valid();
這項檢查是從PMD規則中借鑒而來的。
- String Literal Equality(嚴格的常量等式比較)
<module name="StringLiteralEquality"/>
檢查在判斷字符串是否相等時是否使用了正確的形式
檢查字符串對象的比較是否使用了==或!=運算符。
解釋:Java新手程序員經常會使用類似於下面的代碼:
if (x == "something")
其實他們是想表達如下的意思:
if ("something".equals(x))
- SuperClone(父類clone方法)
<module name="SuperClone"/>
檢查一個覆蓋的clone()方法是否調用了super.clone()方法。
參考:Object.clone()。
- SuperFinalize(父類finalize方法)
<module name="SuperFinalize"/>
檢查一個覆蓋的finalize()方法是否調用了super.finalize()方法。
參考:清理未使用對象。
- Trailing Array Comma(數組尾隨逗號)
<module name="ArrayTrailingComma"/>
檢查初始化數祖時,最后一個元素后面是否加了逗號,如果左右大括號都在同一行,則可以不加逗號
檢查數組的初始化是否包含一個尾隨逗號。
int[] a = new int[]
{
1,
2,
3,
};
如果左花括號和右花括號都位於同一行,那么這項檢查允許不添加尾隨逗號。如下所示:
return new int[] { 0 };
解釋:添加尾隨逗號可以使得改變元素順序,或者在末尾添加新的元素變得更加方便。
- Unnecessary Parentheses(不必要的圓括號)
<module name="UnnecessaryParentheses"/>
檢查代碼中是否使用了不必要的圓括號。
- One Statement Per Line(每行一條語句)
檢查每行是否只有一條語句。下面的一行將會被標識為出錯:
x = 1; y = 2; // 一行中有兩條語句
12. Class Design(類設計:8個)
- Designed For Extension(設計擴展性)
<module name="DesignForExtension"/>
檢查類是否被設計為可擴展的,如果是,則方法應該abstract、final或者是空的
檢查類是否具有可擴展性。更准確地說,它強制使用一種編程風格,父類必須提供空的“句柄”,以便於子類實現它們。
確切的規則是,類中可以由子類繼承的非私有、非靜態方法必須是:
1. abstract方法,或
2. final方法,或
3. 有一個空的實現
解釋:這種API設計風格可以保護父類不會被子類破壞。不利之處在於子類的靈活性會受到限制,特別是它們不能夠阻止父類代碼的執行,但是這也意味着子類不會由於忘記調用父類的方法而破壞父類的狀態。(個人理解:不允許類的方法被子類覆蓋)
- Final Class(final類)
<module name="FinalClass"/>
檢查一個只有私有構造器的類是否被聲明為final。
- Inner Type Last(最后聲明內部類型)
檢查嵌套/內部的類型是否在當前類的最底部聲明(在所有的方法/字段的聲明之后)。
- Hide Utility Class Constructor(隱藏工具類構造器)
<module name="HideUtilityClassConstructor"/>
只定義了靜態方法的類不應該定義一個公有的構造器
確保工具類(在API中只有靜態方法和字段的類)沒有任何公有構造器。
解釋:實例化工具類沒有任何意義。因此,工具類的構造器應當是私有的或者受保護的(如果你打算以后擴展子類)。一個常見的錯誤便是忘記隱藏默認構造器。
如果你打算將工具類的構造器聲明為受保護的,那么你可以考慮下面的構造器實現技術,借此可以禁止子類的實例化:
public class StringUtils // 不是final類,允許子類繼承
{
protected StringUtils() {
throw new UnsupportedOperationException(); // 防止子類調用
}
public static int count(char c, String s) {
// ...
}
}
- Interface Is Type(接口是類型)
<module name="InterfaceIsType"> <property name="allowMarkerInterfaces" value="true"/> </module>
檢查接口是否只定義了變量而沒有定義方法,因為接口應該用來描述一個類型,所以只定義變量而不定義方法是不恰當的
allowMarkerInterfaces: 是否檢查空接口
Bloch編寫的《Effective Java》中提到,接口應當描述為一個類型。因此,定義一個只包含常量,但是沒有包含任何方法的接口是不合適的。標准類javax.swing.SwingConstants是一個會被這項檢查標記的示例類。
這項檢查還可以配置為禁用標記接口,例如java.io.Serializable,這種接口不會包含任何方法或常量。
- Mutable Exception(可變異常)
確保異常(異常類的名稱必須匹配指定的正則表達式)是不可變的。也就是說,異常只能有final字段。
這項檢查當前使用的算法非常簡單,它會檢查異常的所有成員是否是final的。用戶仍然可以修改一個異常實例(例如,Throwable使用setStackTrace(StackTraceElement[] stackTrace)方法修改堆棧跟蹤)。但是,至少這種異常類型所提供的信息是不可修改的。
解釋:異常實例應當表示一個錯誤狀態。異常類中含有非final的字段,不僅僅會導致異常狀態會由於偶然的因素被修改,這樣便會遮蔽原始的異常狀態,還會使得開發者偶爾會忘記初始化異常狀態,這樣便會導致代碼捕捉到異常之后,根據異常狀態推導出不正確的結論。
- Throws Count(拋出計數)
<module name="ThrowsCount"> <property name="max" value="7"/> </module>
將異常拋出語句的數量配置為一個指定的限值(默認值為1)。
解釋:異常是方法接口的組成部分之一。如果一個方法聲明拋出過多不同的異常,就會使得異常處理非常繁重,並且會導致不好的編程習慣,例如catch (Exception)。這項檢查會強制開發者將異常處理變得具有層次性,舉個最簡單的例子,調用者只需要檢查一種類型的異常,但是必要時也允許捕捉上述異常的任何子類。
- Visibility Modifier(可見性標識符)
<module name="VisibilityModifier"> <property name="packageAllowed" value="false"/> <property name="protectedAllowed" value="false"/> <property name="publicMemberPattern" value="^seriaVersionUID$"/> </module>
檢查變量是否對外部可見
packageAllowed: 變量包內成員可以訪問
protectedAllowed: 變量是受保護的
publicMemberPattern: 可以公開訪問的變量所匹配的命名形式
檢查類成員的可見性。只有static final的類成員可以是公有的,其他的類成員必須是私有的,除非設置了protectedAllowed屬性或packageAllowed屬性。
如果類成員的名稱和指定的公有成員正則表達式匹配,那么這項檢查就不會標記這個類成員(默認包含“^serialVersionUID$”)。
解釋:強制封裝。
13. Duplicates(重復:1個)
- Strict Duplicate Code(嚴格重復代碼)
<module name="StrictDuplicateCode"> <property name="min" value="7"/> <property name="charset" value="UTF-8"/> </module>
檢查類中是否有代碼復制的問題
min: 允許代碼重復的最小行數
charset: 文件所用的字符集
逐行地比較所有的代碼行,如果有若干行只有縮進有所不同,那么就報告存在重復代碼。Java代碼中的所有的import語句都會被忽略,任何其他的行 —— 包括Javadoc、方法之間的空白行,等等 —— 都會被檢查(這也是為什么這項檢查被稱作是嚴格的)。
14. Metrics(度量:6個)
- Boolean Expression Complexity(布爾表達式復雜度)
<module name="BooleanExpressionComplexity"> <property name="max" value="7"/> </module>
max: 布爾運算符在一條語句中允許出現的最大數目
限制一個表達式中的&&、||、&、|、^等邏輯運算符的數量。
解釋:過多的條件會導致代碼難以讀懂、調試和維護。
注意,&和|運算符並不僅僅是整數的位運算符,它們還是布爾運算符&&和||的非快捷版本。
- Class Data Abstraction Coupling(類的數據抽象耦合)
<module name="ClassDataAbstractionCoupling"> <property name="max" value="7"/> </module>
這項度量會測量給定類中的其他類的實例化操作的次數。這種類型的耦合並不是由於繼承或者面向對象范型而產生的。一般而言,任何將其他抽象數據類型作為成員的抽象數據類型都具有數據抽象耦合;因此,如果一個類中的某個局部變量是另一個類的實例(對象),那么就存在數據抽象耦合(DAC)。DAC越高,系統的數據結構(類)就會越復雜。
- Class Fan Out Complexity(類的扇出復雜度)
一個給定類所依賴的其他類的數量。這個數量的平方還可以用於表示函數式程序(基於文件)中需要維護總量的最小值。
- Cyclomatic Complexity(循環復雜度)
<module name="CyclomaticComplexity"> <property name="severity" value="ignore"/> </module>
檢查循環復雜度是否超出了指定的限值。該復雜度由構造器、方法、靜態初始化程序、實例初始化程序中的if、while、do、for、?:、catch、switch、case等語句,以及&&和||運算符的數量所測量。它是遍歷代碼的可能路徑的一個最小數量測量,因此也是需要的測試用例的數量。通常1-4是很好的結果,5-7較好,8-10就需要考慮重構代碼了,如果大於11,則需要馬上重構代碼!
- Non Commenting Source Statements(非注釋源碼語句)
通過對非注釋源碼語句(NCSS)進行計數,確定方法、類、文件的復雜度。這項檢查遵守Chr. Clemens Lee編寫的JavaNCSS-Tool中的規范。
粗略地說,NCSS度量就是不包含注釋的源代碼行數,(近似)等價於分號和左花括號的計數。一個類的NCSS就是它所有方法的NCSS、它的內部類的NCSS、成員變量聲明數量的總和。一個文件的NCSS就是它所包含的所有頂層類的NCSS、imports語句和包聲明語句數量的總和。
解釋:太大的方法和類會難以閱讀,並且維護成本會很高。一個較大的NCSS數值通常意味着對應的方法或類承擔了過多的責任和/或功能,應當分解成多個較小的單元。
- NPath Complexity(NPath復雜度)
NPATH度量會計算遍歷一個函數時,所有可能的執行路徑的數量。它會考慮嵌套的條件語句,以及由多部分組成的布爾表達式(例如,A && B,C || D,等等)。
解釋:在Nejmeh的團隊中,每個單獨的例程都有一個取值為200的非正式的NPATH限值;超過這個限值的函數可能會進行進一步的分解,或者至少一探究竟。
15. Miscellaneous(雜項:12個)
- Array Type Style(數組類型風格)
<module name="ArrayTypeStyle"> <property name="javaStyle" value="true"/> </module>
javaStyle: 定義是否采用java風格定義數組
檢查數組定義的風格。有的開發者使用Java風格:public static void main(String[] args);有的開發者使用C風格:public static void main(String args[])。
- Descendent Token Check(后續標記檢查)
檢查在其他標記之下的受限標記。
警告:這是一項非常強大和靈活的檢查,但是與此同時,它偏向於底層技術,並且非常依賴於具體實現,因為,它的結果依賴於我們用來構建抽象語法樹的語法。因此,當其他檢查項目提供了你想要用的功能時,我們建議你使用這些檢查項目。總之,這項檢查只能在抽象語法樹的層面上工作,它並不了解任何語言結構。
- Final Parameters(final參數)
<module name="FinalParameters"/>
tokens: 定義檢查的類型
檢查方法/構造器的參數是否是final的。這項檢查會忽略接口方法的檢查 —— final關鍵字不會理會接口方法的參數,因為沒有任何代碼能夠修改這些參數。
解釋:在方法算法的執行期間改變參數值會使讀者產生混淆,因此應當避免這種情況的發生。有個很好的方法可以使得Java的編譯器預防這種編碼風格,那就是將方法的參數聲明為final的。
- Indentation(代碼縮進)
<module name="Indentation"> <property name="basicOffset" value="4"/> <property name="braceAdjustment" value="0"/> <property name="caseIndent" value="4"/> </module>
basicOffset: 定義代碼體相對於所屬的代碼體的縮進量
braceAdjustment: 定義括號的縮進量
caseIndent: 定義case的縮進量
檢查Java代碼的縮進是否正確。
盡管有些格式精美的打印機有時可以很方便地批量重排原始代碼的格式,但是它們通常不是沒有足夠的可配置性,就是不能夠達到預期的排版格式。有時這是個人喜好的問題,有時這是實際經驗的問題。無論如何,這項檢查應當只確保代碼遵守縮進規則的一個最小集合。
- New Line At End Of File(文件末尾的新行)
<module name="NewlineAtEndOfFile"/>
lineSeparator: 行分隔符的類型,windows是crlf
檢查文件是否以新行結束。
解釋:通常,任何源碼文件和文本文件都應當以一個新行符結束,特別是使用諸如CVS這樣的SCM系統時。當文件沒有以新行結束時,CVS甚至會打印出一個警告。
- Todo Comment(TODO注釋)
這項檢查負責TODO注釋的檢查。實際上,這是一種檢查Java注釋的通用正則表達式匹配器。想要檢查其他格式的Java注釋,那么設置format屬性即可。
- Translation(語言轉換)
<module name="Translation"> <property name="severity" value="info"/> </module>
這是一項FileSetCheck檢查,通過檢查關鍵字的一致性屬性文件,它可以確保代碼的語言轉換的正確性。可以使用兩個描述同一個上下文環境的屬性文件來保證一致性,如果它們包含相同的關鍵字。
考慮下面的屬性文件,它們在同一個目錄中:
#messages.properties
hello=Hello
cancel=Cancel
#messages_de.properties
hell=Hallo
ok=OK
轉換檢查將會發現德語hello關鍵字的拼寫錯誤、默認資源文件(messages.properties)缺少ok關鍵字、德語資源文件(messages_de.properties)缺少cancel關鍵字等錯誤:
messages_de.properties: Key 'hello' missing.
messages_de.properties: Key 'cancel' missing.
messages.properties: Key 'hell' missing.
messages.properties: Key 'ok' missing.
- Trailing Comment(行尾注釋)
這項檢查可以確保代碼中含有注釋的行中只包含注釋。在使用//注釋的場合下,這意味着//符號之前只能有空格。如果行不是以注釋結束的,那么就不會檢查這些注釋。例如,下面的代碼是可接受的
Thread.sleep( 10 );
format屬性會處理“} // while”這樣的注釋。
解釋:Steve McConnell編寫的《Code Complete》認為行尾注釋是一個不好的編程習慣。行尾注釋就是那些和實際代碼位於同一行的注釋。例如:
a = b + c; // 一條常見的注釋
d = e / f; // 這一行的另一條注釋
《Code Complete》為此給出了以下幾條論證:
1. 注釋必須對齊,這樣便不會干擾代碼的可視結構。如果你不將它們整潔地對齊,它們將會使你的代碼看起來就像剛從洗衣機里出來一樣亂糟糟的。
2. 行尾注釋會很難格式化,需要花費大量的時間來對齊它們。這樣的時間並不是花在深入理解代碼上的,你只能乏味地敲擊空格鍵或制表鍵來重新格式化這些注釋。
3. 行尾注釋非常難以維護。如果某一行包含行尾注釋的代碼增加了,就會使這行的注釋被擠得更遠,並且所有其他的行尾注釋為了排版對齊,不得不被放置的同樣遠。難以維護的代碼風格就是不可維護的。
4. 行尾注釋可能會意義不明確。每行的右側通常不能提供足夠的空間來放置注釋,將注釋和代碼放在同一行就意味着注釋可能會比較短。按照這種習慣,你編寫代碼時就會專注於每行盡可能的短,而不是每行盡可能的清晰。因此,這種注釋經常會意義不明確清晰。
5. 行尾注釋還會帶來一個系統性問題,你會發現很難僅僅在一行中寫出意義明確的注釋。大多數行尾注釋僅僅重復了一下這行的代碼,這種行為帶來的危害性遠比帶來的幫助要大。
當使用自動化重構技術時,源碼每行的長度會經常變化,這就使得包含大量行尾注釋的代碼變得非常難以維護。
- Uncommented Main(未注釋main方法)
<module name="UncommentedMain"> <property name="excludedClasses" value="^$"/> </module>
excludedClasses: 定義可以帶main方法的類所匹配的名字形式
檢查源碼中是否有未注釋的main()方法(調試的殘留物)。
解釋:調試時經常會在代碼中利用main()方法。當調試結束時,開發者經常會忘記刪除這些main()方法,這樣會改變API,並且會增大生成的class/jar文件的尺寸。除了程序真正的入口點之外,源碼中其他所有的main()方法都應當被刪除或注釋掉。
- Upper Ell(大寫“L”)
<module name="UpperEll"/>
檢查long類型的常量在定義時是否由大寫的“L”開頭。注意,是“L”,不是“l”。這是由《Java Language Specification》的第3.10.1章節所建議的編碼規約。
解釋:小寫字母“l”看起來非常像數字“1”。
- Regexp(正則表達式)
這項檢查可以確保指定的格式串在文件中存在,或者允許出現幾次,或者不存在。
這項檢查結合了RegexpHeader、GenericIllegalRegexp和RequiredRegexp的所有功能,但是並不支持使用文件中正則表達式。
這項檢查的不同之處在於,它是工作在多行模式下的。它的正則表達式可以跨越多行,並且可以一次性檢查完整個文件。上面提到的其他三項檢查工作在單行模式下。它們的單行或多行正則表達式一次只能檢查一行。它們只能依次檢查文件中的每一行。
注意:由於工作模式有所不同,所以需要對使用的正則表達式做一些修改才行。
在多行模式下:
1. “^”符號表示一行的開始,而不是輸入的開始。
2. “\A”表示輸入的開始。
3. “$”符號表示一行的結束,而不是輸入的結束。
4. “\Z”表示輸入的結束。
5. 文件中的每行都是以新行符號結束。
注意:並不是所有的正則表達式引擎創建正則表達式都是相同的。有些引擎提供了額外的功能,但是其他的引擎不支持,並且語法元素可能也有所不同。這項檢查使用了java.util.regex包,請查看相關文檔,以便於學習如何構建一個符合使用目標的正則表達式。
注意:當你在XML配置文件中鍵入一個正則表達式作為參數時,你必須考慮到XML文件的規則,例如,如果你想要匹配一個“<”符號,那么你需要鍵入“<”。一條正則表達式應當在同一行中鍵入。
- Outer Type File Name(外部類型文件名)
檢查外部類型名稱是否與文件名稱匹配。例如,類Foo必須在文件Foo.java中。
16. Other(其他:2個)
- Checker(檢查器)
每個checkstyle配置的根模塊。不能被刪除。
- TreeWalker(樹遍歷器)
FileSetCheck TreeWalker會檢查單個的Java源碼文件,並且定義了適用於檢查這種文件的屬性。
17. Filters(過濾器:4個)
- Severity Match Filter(嚴重度匹配過濾器)
SeverityMatchFilter過濾器會根據事件的嚴重級別決定是否要接受審計事件。
- Suppression Filter(抑制過濾器)
<module name="SuppressionFilter"> <property name="file" value="${check.filter.file}"/> </module>
在檢查錯誤時,SuppressionFilter過濾器會依照一個XML格式的策略抑制文件,選擇性地拒絕一些審計事件。如果沒有配置好的策略抑制文件可用,那么這個過濾器會接受所有的審計事件。
- Suppression Comment Filter(抑制注釋過濾器)
SuppressionCommentFilter過濾器使用配對的注釋來抑制審計事件。
解釋:有時候,違反一項檢查是有正當理由的。當問題出在代碼本身,而不是個人喜好時,最好在代碼中覆蓋檢查策略。半結構化的注釋可以和檢查關聯起來。這種做法有時比一個獨立的策略抑制文件更好,因為這個文件必須隨着源碼文件的變化而保持更新。
- Suppress With Nearby Comment Filter(抑制附近注釋過濾器)
SuppressWithNearbyCommentFilter過濾器使用獨立的注釋來抑制審計事件。
解釋:和SuppressionCommentFilter相同。然而,SuppressionCommentFilter使用配對的過濾器來打開/關閉注釋匹配,SuppressWithNearbyCommentFilter則使用單個注釋過濾器。這樣可以使用更少的行數來標記一塊區域,在某些環境中會顯得風格很優美。
用法:這個過濾器需要和FileContentsHolder協同使用,因為檢查器可以在.java文件中不公開地使用抑制注釋。包含這個過濾器的配置文件必須將FileContentsHolder配置為TreeWalker的一個子模塊。
