1. 程式人生 > >詳解CheckStyle的檢查規則(共138條規則)

詳解CheckStyle的檢查規則(共138條規則)

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(方法註釋)
檢查方法或構造器的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(風格註釋)
驗證Javadoc註釋,以便於確保它們的格式。可以檢查以下注釋:介面宣告、類宣告、方法宣告、構造器宣告、變數宣告。
Type Javadoc(型別註釋)
檢查方法或構造器的Javadoc。預設不會檢查author和version標記。想要驗證的可見範圍是由scope屬性指定的,預設值為Scope.PRIVATE。想要驗證其他的可見範圍,則需要將scope屬性設定為相應的值。想要為author標記或version標記定義格式,將authorFormat屬性或versionFormat屬性設定為指定的正則表示式即可。
如果不想顯示由於型別引數沒有在註釋中使用param標記進行說明而產生的錯誤訊息,就需要勾選allowMissingParamTags屬性。
Variable Javadoc(變數註釋)
檢查變數是否具有Javadoc註釋。
Write Tag(輸出標記)
將Javadoc作為資訊輸出。可以作為樣式表來使用,根據作者名排序報告。想要為一個標記定義格式,將tagFormat屬性設定為相應的正則表示式即可。這項檢查會使用兩種不同的嚴重級別。當標記缺失時,使用標準的嚴重級別(Severity)。當標記存在時,使用附加的嚴重級別(tagSeverity)作為報告等級。

3. Naming Conventions(命名規約:12個)

Abstract Class Name(抽象類名稱)
檢查抽象類的名稱是否遵守命名規約。
Class Type Parameter Name(類的型別引數名稱)
檢查類的型別引數名稱是否遵守命名規約。
Constant Names(常量名稱)
檢查常量(用static final修飾的欄位)的名稱是否遵守命名規約。
Local Final Variable Names(區域性final變數名稱)
檢查區域性final變數的名稱是否遵守命名規約。
Local Variable Names(區域性變數名稱)
檢查區域性變數的名稱是否遵守命名規約。
Member Names(成員名稱)
檢查成員變數(非靜態欄位)的名稱是否遵守命名規約。
Method Names(方法名稱)
檢查方法名稱是否遵守命名規約。
Method Type Parameter Name(方法的型別引數名稱)
檢查方法的型別引數名稱是否遵守命名規約。
Package Names(包名稱)
檢查包名稱是否遵守命名規約。
Parameter Names(引數名稱)
檢查引數名稱是否遵守命名規約。
Static Variable Names(靜態變數名稱)
檢查靜態變數(用static修飾,但沒用final修飾的欄位)的名稱是否遵守命名規約。
Type Names(型別名稱)
檢查類的名稱是否遵守命名規約。

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}line2://checkstyle:
line 3: ^// Checks Java source code for adherence to a set of rules.line4://CopyrightC\d\d\d\dOliverBurn
line 5: ^// Last modification by $Author.*$line6:/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(避免萬用字元匯入)
檢查沒有import語句使用*符號。
解釋:從一個包中匯入所有的類會導致包之間的緊耦合,當一個新版本的庫引入了命名衝突時,這樣就有可能導致問題發生。
Avoid Static Imports(避免靜態匯入)
檢查沒有靜態匯入語句。
解釋:匯入靜態成員可能會導致類成員之間的命名衝突。匯入靜態成員可能會導致程式碼的可讀性很差,因為讀者可能會搞不清楚成員到底位於哪個類中。
Illegal Imports(非法匯入)
檢查是否匯入了指定的非法包。預設情況下,這項檢查會拒絕所有的sun.包,因為直接使用sun.包的程式肯定不是100%的純Java程式。想要拒絕其他的包,將illegalPkgs屬性設定為你指定的非法包列表即可。
Import Order Check(匯入順序檢查)
檢查匯入包的順序/分組。確保匯入包的分組按照指定的順序排列(例如,java.排在首位,javax.排在第二,以此類推),並且每個分組內匯入的包都是按照字典序排列的。靜態匯入必須放在最後,並且也是按照字典序排列的。
Redundant Imports(多餘匯入)
檢查是否存在多餘的匯入語句。如果一條匯入語句滿足以下條件,那麼就是多餘的:
1. 它是另一條匯入語句的重複。也就是,一個類被匯入了多次。
2. 從java.lang包中匯入類,例如,匯入java.lang.String。
3. 從當前包中匯入類。
Unused Imports(未使用匯入)
檢查未使用的匯入語句。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(匿名內部類長度)
檢查匿名內部類的長度。
解釋:如果一個匿名內部類變得非常長,那麼就很難讀懂它,並且難以跟蹤方法的流程。因此,過長的匿名內部類通常應當被重構為一個具名內部類。可以參考Bloch編寫的《Effective Java》的第93頁。
Executable Statement Size(可執行語句數量)
將可執行語句的數量限制為一個指定的限值。
Maximum File Length(最大檔案長度)
檢查原始碼檔案的長度。
解釋:如果一個原始碼檔案變得非常長,那麼就會難以理解。因此,過長的類通常應當被重構成若干個較小的獨立的類,每個類專注於一個特定的任務。
Maximum Line Length(最大行長度)
檢查原始碼每行的長度。
解釋:將原始碼打印出來,或者開發者限制了螢幕顯示原始碼的空間(例如,IDE會顯示額外的資訊,諸如專案樹、類層次等等),那麼過長的行會顯得難以閱讀。
Maximum Method Length(最大方法長度)
檢查方法和構造器的長度。
解釋:如果一個方法變得非常長,那麼就會難以理解。因此,過長的方法通常應當被重構成若干個較小的獨立的方法,每個方法專注於一個特定的任務。
Maximum Parameters(最大引數數量)
檢查一個方法或構造器的引數的數量。
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初始化語句填充符)
檢查空的for迴圈初始化語句的填充符,也就是空格是否可以作為for迴圈初始化語句空位置的填充符。如果程式碼自動換行,則不會進行檢查,正如以下程式碼所示:
for (
; i < j; i++, j–)
Empty For Iterator Pad(空白for迭代器填充符)
檢查空的for迴圈迭代器的填充符,也就是空格是否可以作為for迴圈迭代器空位置的填充符。如果程式碼自動換行,則不會進行檢查,正如以下程式碼所示:
for (Iterator foo = very.long.line.iterator();
foo.hasNext();
)
No Whitespace After(指定標記之後沒有空格)
檢查指定標記之後沒有空格。若要禁用指定標記之後的換行符,將allowLineBreaks屬性設為false即可。
No Whitespace Before(指定標記之前沒有空格)
檢查指定標記之前沒有空格。若要允許指定標記之前的換行符,將allowLineBreaks屬性設為true即可。
Operator Wrap(運算子換行)
檢查程式碼自動換行時,運算子所處位置的策略。nl表示運算子必須在新行中,eol表示運算子必須在當前行的行末。
Method Parameter Pad(方法引數填充符)
檢查方法定義、構造器定義、方法呼叫、構造器呼叫的識別符號和引數列表的左圓括號之間的填充符。也就是,如果識別符號和左圓括號位於同一行,那麼就檢查識別符號之後是否需要緊跟一個空格。如果識別符號和左圓括號不在同一行,那麼就報錯,除非將規則配置為允許使用換行符。想要在識別符號之後使用換行符,將allowLineBreaks屬性設定為true即可。
Paren Pad(圓括號填充符)
檢查圓括號的填充符策略,也就是在左圓括號之後和右圓括號之前是否需要有一個空格。
Typecast Paren Pad(型別轉換圓括號填充符)
檢查型別轉換的圓括號的填充符策略。也就是,在左圓括號之後和右圓括號之前是否需要有一個空格。
File Tab Character(檔案製表符)
檢查原始碼中沒有製表符(’\t’)。

解釋:
1. 為了能夠更方便地閱讀原始碼,開發者不應當在他們的文字編輯器中配置製表符的寬度。
2. 根據Apache Jakarta的編碼標準:在一個分散式開發環境中,當提交的訊息被髮送到一個郵件列表中時,如果你使用了製表符,那麼這些訊息會變得幾乎不可能閱讀。
Whitespace After(指定標記之後有空格)
檢查指定標記之後是否緊跟了空格。
Whitespace Around(指定標記周圍有空格)
檢查指定標記的周圍是否有空格。以下形式的空構造器和方法的程式碼體(程式碼塊):
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(修飾符順序)
檢查程式碼中的識別符號的順序是否符合《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(多餘修飾符)
在以下部分檢查是否有多餘的修飾符:
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(避免巢狀程式碼塊)
找到巢狀程式碼塊,也就是在程式碼中無節制使用的程式碼塊。
解釋:內嵌程式碼塊通常是除錯過程的殘留物,它們會使讀者產生混淆。
Empty Block(空程式碼塊)
檢查空程式碼塊。
Left Curly Brace Placement(左花括號位置)
檢查程式碼塊的左花括號的放置位置。
通過property選項指定驗證策略。
若使用eol和nlow策略,則需要考慮maxLineLength屬性。
Need Braces(需要花括號)
檢查程式碼塊周圍是否有大括號,可以檢查do、else、if、for、while等關鍵字所控制的程式碼塊。
Right Curly Brace Placement(右花括號位置)
檢查else、try、catch標記的程式碼塊的右花括號的放置位置。
通過property選項指定驗證策略。

11. Coding Problems(編碼問題:43個)

Avoid Inline Conditionals(避免內聯條件語句)
檢測內聯條件語句。內聯條件語句的一個示例如下所示:
String a = getParameter(“a”);
String b = (a==null || a.length<1) ? null : a.substring(1);
解釋:有些開發者發現內聯條件語句很難讀懂,因此他們公司的編碼標準會禁止使用內聯條件語句。
Covariant Equals(共變equals方法)
檢查定義了共變equals()方法的類中是否同樣覆蓋了equals(java.lang.Object)方法。這項檢查受到FindBugs的啟發。
解釋:錯誤地定義了一個共變equals()方法,而沒有覆蓋equals(java.lang.Object)方法,可能會產生不可預料的執行時行為。
Default Comes Last(預設分支置於最後)
檢查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方法)
檢查覆蓋了equals()方法的類是否也覆蓋了hashCode()方法。
解釋:equals()方法和hashCode()方法約定,相等的物件必然具有相同的雜湊碼。因此,只要你覆蓋了equals()方法,你就必須同時覆蓋hashCode()方法,以確保可以在基於雜湊的集合中使用你的類。
Explicit Initialization(顯式初始化)
檢查類或物件的成員是否顯式地初始化為成員所屬型別的預設值(物件引用的預設值為null,數值和字元型別的預設值為0,布林型別的預設值為false)。
解釋:每個例項變數都會被初始化兩次,並且初始化為相同的值。在執行程式碼中指定的任何初始化操作之前,Java會初始化每個例項變數為它的預設值(0或null)。因此在這種情況下,x會被初始化為0兩次,bar會被初始化為null兩次。因此,這樣稍微有些效率低下。這種編碼風格是C/C++編碼風格的延續,它表明開發者並不是真正有把握Java能夠初始化例項變數為它的預設值。
Fall Through(跨越分支)
檢查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區域性變數)
檢查從未改變取值的區域性變數是否被宣告為final。這項檢查還可以被配置為檢查未修改過的引數是否被宣告為final。
當配置為檢查引數時,這項檢查會忽略介面方法和抽象方法中的引數。
Hidden Field(隱藏欄位)
檢查區域性變數或引數是否會遮蔽在相同類中定義的欄位。
Illegal Instantiation(非法例項化)
檢查是否有不合法的例項化操作,是否使用工廠方法更好。
解釋:根據不同的專案,對於某些類來說,可能通過工廠方法來建立類例項更好,而不是呼叫類構造器。
一個簡單的示例就是java.lang.Boolean類。為了節省記憶體和CPU週期,最好使用預定義的常量TRUE和FALSE。構造器的呼叫應當被替換為呼叫Boolean.valueOf()方法。

某些對效能有極端要求的專案可能需要其他的類也使用工廠方法,以便於提高快取或物件池的使用效率。
Illegal Catch(非法異常捕捉)
從不允許捕捉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預設分支)
檢查switch語句是否含有default子句。
解釋:在每個switch語句中引入一條預設分支通常是一個很好的主意。即使開發者確信所有當前可能的分支都能覆蓋到,這也應當在default分支中表達出來,例如,使用一條斷言。這種方法使得程式碼可以應付以後的修改,例如,在一個列舉型別中引入新的型別。
Modified Control Variable(修改控制變數)
檢查確保for迴圈的控制變數沒有在for程式碼塊中被修改。示例程式碼如下:
for (int i = 0; i < 1; i++) {
i++;
}
解釋:如果在迴圈體中修改了控制變數,程式流程就會變得更加難以跟蹤。可以用while迴圈替換for迴圈。
Multiple String Literals(多重字串常量)
檢查在單個檔案中,相同的字串常量是否出現了多次。
解釋:重複程式碼會使得維護工作變得更加困難,因此最好用一個常量來替換多次出現。
Multiple Variable Declaration(多重變數宣告)
檢查每個變數是否使用一行一條語句進行宣告。
解釋:《SUN編碼規約》的第6.1章節推薦應當使用一行一條語句宣告一個變數。
Nested For Depth(for巢狀深度)
限制for迴圈的巢狀層數(預設值為1)。
Nested If Depth(if巢狀深度)
限制if-else程式碼塊的巢狀層數(預設值為1)。
Nested Try Depth(try巢狀深度)
限制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(包宣告)
確保一個類具有一個包宣告,並且(可選地)包名要與原始碼檔案所在的目錄名相匹配。
解釋:位於空包中的類是不能夠被匯入的。很多開發新手並沒有注意到這一點。
Parameter Assignment(引數賦值)
不允許對引數進行賦值。
解釋:對引數的賦值通常被認為是缺乏程式設計實踐經驗。強迫開發者將引數宣告為final通常是非常麻煩的。這項檢查可以確保引數從不會被賦值,這對於雙方都是好事。
Redundant Throws(多餘的throws)
檢查throws子句中是否聲明瞭多餘的異常,例如重複異常、未檢查的異常或一個已宣告丟擲的異常的子類。
Require This(需要this)
檢查程式碼是否使用了“this.”,也就是說,在預設情況下,引用當前物件的例項變數和方法時,應當顯式地通過“this.varName”或“this.methodName(args)”這種形式進行呼叫。
Return Count(return總數)
限制return語句的數量。預設值為2。可以忽略檢查指定的方法(預設忽略equals()方法)。
解釋:過多的返回點可能表明程式碼嘗試處理過多的業務,可能會難以理解。
Simplify Boolean Expression(簡化布林表示式)
檢查是否有過於複雜的布林表示式。現在能夠發現諸如if (b == true)、b || true、!false等型別的程式碼。
解釋:複雜的布林邏輯會使得程式碼難以理解和維護。
Simplify Boolean Return(簡化布林返回值)
檢查是否有過於複雜的布林型別return語句。例如下面的程式碼:
if (valid())
return false;
else
return true;
可以寫成:
return !valid();
這項檢查是從PMD規則中借鑑而來的。
String Literal Equality(嚴格的常量等式比較)
檢查字串物件的比較是否使用了==或!=運算子。
解釋:Java新手程式設計師經常會使用類似於下面的程式碼:
if (x == “something”)
其實他們是想表達如下的意思:
if (“something”.equals(x))
SuperClone(父類clone方法)
檢查一個覆蓋的clone()方法是否呼叫了super.clone()方法。
參考:Object.clone()。
SuperFinalize(父類finalize方法)
檢查一個覆蓋的finalize()方法是否呼叫了super.finalize()方法。
參考:清理未使用物件。
Trailing Array Comma(陣列尾隨逗號)
檢查陣列的初始化是否包含一個尾隨逗號。
int[] a = new int[]
{
1,
2,
3,
};
如果左花括號和右花括號都位於同一行,那麼這項檢查允許不新增尾隨逗號。如下所示:
return new int[] { 0 };

解釋:新增尾隨逗號可以使得改變元素順序,或者在末尾新增新的元素變得更加方便。
Unnecessary Parentheses(不必要的圓括號)
檢查程式碼中是否使用了不必要的圓括號。
One Statement Per Line(每行一條語句)
檢查每行是否只有一條語句。下面的一行將會被標識為出錯:
x = 1; y = 2; // 一行中有兩條語句

12. Class Design(類設計:8個)

Designed For Extension(設計擴充套件性)
檢查類是否具有可擴充套件性。更準確地說,它強制使用一種程式設計風格,父類必須提供空的“控制代碼”,以便於子類實現它們。
確切的規則是,類中可以由子類繼承的非私有、非靜態方法必須是:
1. abstract方法,或
2. final方法,或
3. 有一個空的實現
解釋:這種API設計風格可以保護父類不會被子類破壞。不利之處在於子類的靈活性會受到限制,特別是它們不能夠阻止父類程式碼的執行,但是這也意味著子類不會由於忘記呼叫父類的方法而破壞父類的狀態。(個人理解:不允許類的方法被子類覆蓋)
Final Class(final類)
檢查一個只有私有構造器的類是否被宣告為final。
Inner Type Last(最後宣告內部型別)
檢查巢狀/內部的型別是否在當前類的最底部宣告(在所有的方法/欄位的宣告之後)。
Hide Utility Class Constructor(隱藏工具類構造器)
確保工具類(在API中只有靜態方法和欄位的類)沒有任何公有構造器。
解釋:例項化工具類沒有任何意義。因此,工具類的構造器應當是私有的或者受保護的(如果你打算以後擴充套件子類)。一個常見的錯誤便是忘記隱藏預設構造器。

如果你打算將工具類的構造器宣告為受保護的,那麼你可以考慮下面的構造器實現技術,藉此可以禁止子類的例項化:
public class StringUtils // 不是final類,允許子類繼承
{
protected StringUtils() {
throw new UnsupportedOperationException(); // 防止子類呼叫
}

public static int count(char c, String s) {
    // ...
}

}
Interface Is Type(介面是型別)
Bloch編寫的《Effective Java》中提到,介面應當描述為一個型別。因此,定義一個只包含常量,但是沒有包含任何方法的介面是不合適的。標準類javax.swing.SwingConstants是一個會被這項檢查標記的示例類。

這項檢查還可以配置為禁用標記介面,例如java.io.Serializable,這種介面不會包含任何方法或常量。
Mutable Exception(可變異常)
確保異常(異常類的名稱必須匹配指定的正則表示式)是不可變的。也就是說,異常只能有final欄位。
這項檢查當前使用的演算法非常簡單,它會檢查異常的所有成員是否是final的。使用者仍然可以修改一個異常例項(例如,Throwable使用setStackTrace(StackTraceElement[] stackTrace)方法修改堆疊跟蹤)。但是,至少這種異常型別所提供的資訊是不可修改的。
解釋:異常例項應當表示一個錯誤狀態。異常類中含有非final的欄位,不僅僅會導致異常狀態會由於偶然的因素被修改,這樣便會遮蔽原始的異常狀態,還會使得開發者偶爾會忘記初始化異常狀態,這樣便會導致程式碼捕捉到異常之後,根據異常狀態推匯出不正確的結論。
Throws Count(丟擲計數)
將異常丟擲語句的數量配置為一個指定的限值(預設值為1)。
解釋:異常是方法介面的組成部分之一。如果一個方法宣告丟擲過多不同的異常,就會使得異常處理非常繁重,並且會導致不好的程式設計習慣,例如catch (Exception)。這項檢查會強制開發者將異常處理變得具有層次性,舉個最簡單的例子,呼叫者只需要檢查一種型別的異常,但是必要時也允許捕捉上述異常的任何子類。
Visibility Modifier(可見性識別符號)
檢查類成員的可見性。只有static final的類成員可以是公有的,其他的類成員必須是私有的,除非設定了protectedAllowed屬性或packageAllowed屬性。
如果類成員的名稱和指定的公有成員正則表示式匹配,那麼這項檢查就不會標記這個類成員(預設包含“^serialVersionUID$”)。
解釋:強制封裝。

13. Duplicates(重複:1個)

Strict Duplicate Code(嚴格重複程式碼)
逐行地比較所有的程式碼行,如果有若干行只有縮排有所不同,那麼就報告存在重複程式碼。Java程式碼中的所有的import語句都會被忽略,任何其他的行 —— 包括Javadoc、方法之間的空白行,等等 —— 都會被檢查(這也是為什麼這項檢查被稱作是嚴格的)。

14. Metrics(度量:6個)

Boolean Expression Complexity(布林表示式複雜度)
限制一個表示式中的&&、||、&、|、^等邏輯運算子的數量。
解釋:過多的條件會導致程式碼難以讀懂、除錯和維護。
注意,&和|運算子並不僅僅是整數的位運算子,它們還是布林運算子&&和||的非快捷版本。
Class Data Abstraction Coupling(類的資料抽象耦合)
這項度量會測量給定類中的其他類的例項化操作的次數。這種型別的耦合並不是由於繼承或者面向物件範型而產生的。一般而言,任何將其他抽象資料型別作為成員的抽象資料型別都具有資料抽象耦合;因此,如果一個類中的某個區域性變數是另一個類的例項(物件),那麼就存在資料抽象耦合(DAC)。DAC越高,系統的資料結構(類)就會越複雜。
Class Fan Out Complexity(類的扇出複雜度)
一個給定類所依賴的其他類的數量。這個數量的平方還可以用於表示函式式程式(基於檔案)中需要維護總量的最小值。
Cyclomatic Complexity(迴圈複雜度)
檢查迴圈複雜度是否超出了指定的限值。該複雜度由構造器、方法、靜態初始化程式、例項初始化程式中的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(陣列型別風格)
檢查陣列定義的風格。有的開發者使用Java風格:public static void main(String[] args);有的開發者使用C風格:public static void main(String args[])。
Descendent Token Check(後續標記檢查)
檢查在其他標記之下的受限標記。
警告:這是一項非常強大和靈活的檢查,但是與此同時,它偏向於底層技術,並且非常依賴於具體實現,因為,它的結果依賴於我們用來構建抽象語法樹的語法。因此,當其他檢查專案提供了你想要用的功能時,我們建議你使用這些檢查專案。總之,這項檢查只能在抽象語法樹的層面上工作,它並不瞭解任何語言結構。
Final Parameters(final引數)
檢查方法/構造器的引數是否是final的。這項檢查會忽略介面方法的檢查 —— final關鍵字不會理會介面方法的引數,因為沒有任何程式碼能夠修改這些引數。
解釋:在方法演算法的執行期間改變引數值會使讀者產生混淆,因此應當避免這種情況的發生。有個很好的方法可以使得Java的編譯器預防這種編碼風格,那就是將方法的引數宣告為final的。
Indentation(程式碼縮排)
檢查Java程式碼的縮排是否正確。
儘管有些格式精美的印表機有時可以很方便地批量重排原始程式碼的格式,但是它們通常不是沒有足夠的可配置性,就是不能夠達到預期的排版格式。有時這是個人喜好的問題,有時這是實際經驗的問題。無論如何,這項檢查應當只確保程式碼遵守縮排規則的一個最小集合。
New Line At End Of File(檔案末尾的新行)
檢查檔案是否以新行結束。
解釋:通常,任何原始碼檔案和文字檔案都應當以一個新行符結束,特別是使用諸如CVS這樣的SCM系統時。當檔案沒有以新行結束時,CVS甚至會打印出一個警告。
Todo Comment(TODO註釋)
這項檢查負責TODO註釋的檢查。實際上,這是一種檢查Java註釋的通用正則表示式匹配器。想要檢查其他格式的Java註釋,那麼設定format屬性即可。
Translation(語言轉換)
這是一項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方法)
檢查原始碼中是否有未註釋的main()方法(除錯的殘留物)。
解釋:除錯時經常會在程式碼中利用main()方法。當除錯結束時,開發者經常會忘記刪除這些main()方法,這樣會改變API,並且會增大生成的class/jar檔案的尺寸。除了程式真正的入口點之外,原始碼中其他所有的main()方法都應當被刪除或註釋掉。
Upper Ell(大寫“L”)
檢查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原始碼檔案,並且定義了適用於檢查這種檔案的屬性。

  1. Filters(過濾器:4個)
    Severity Match Filter(嚴重度匹配過濾器)
    SeverityMatchFilter過濾器會根據事件的嚴重級別決定是否要接受審計事件。
    Suppression Filter(抑制過濾器)
    在檢查錯誤時,SuppressionFilter過濾器會依照一個XML格式的策略抑制檔案,選擇性地拒絕一些審計事件。如果沒有配置好的策略抑制檔案可用,那麼這個過濾器會接受所有的審計事件。
    Suppression Comment Filter(抑制註釋過濾器)
    SuppressionCommentFilter過濾器使用配對的註釋來抑制審計事件。

解釋:有時候,違反一項檢查是有正當理由的。當問題出在程式碼本身,而不是個人喜好時,最好在程式碼中覆蓋檢查策略。半結構化的註釋可以和檢查關聯起來。這種做法有時比一個獨立的策略抑制檔案更好,因為這個檔案必須隨著原始碼檔案的變化而保持更新。
Suppress With Nearby Comment Filter(抑制附近註釋過濾器)
SuppressWithNearbyCommentFilter過濾器使用獨立的註釋來抑制審計事件。

解釋:和SuppressionCommentFilter相同。然而,SuppressionCommentFilter使用配對的過濾器來開啟/關閉註釋匹配,SuppressWithNearbyCommentFilter則使用單個註釋過濾器。這樣可以使用更少的行數來標記一塊區域,在某些環境中會顯得風格很優美。

用法:這個過濾器需要和FileContentsHolder協同使用,因為檢查器可以在.java檔案中不公開地使用抑制註釋。包含這個過濾器的配置檔案必須將FileContentsHolder配置為TreeWalker的一個子模組。