1. Annotations(注解:5個(gè))
- Annotation Use Style(注解使用風(fēng)格)
這項(xiàng)檢查可以控制要使用的注解的樣式要销。
- Missing Deprecated(缺少deprecad)
檢查java.lang.Deprecated注解或@deprecated的Javadoc標(biāo)記是否同時(shí)存在纤掸。
- Missing Override(缺少override)
當(dāng)出現(xiàn){@inheritDoc}的Javadoc標(biāo)簽時(shí)借跪,驗(yàn)證java.lang.Override注解是否出現(xiàn)掏愁。
- Package Annotation(包注解)
這項(xiàng)檢查可以確保所有包的注解都在package-info.java文件中果港。
- Suppress Warnings(抑制警告)
這項(xiàng)檢查允許你指定不允許SuppressWarnings抑制哪些警告信息辛掠。你還可以指定一個(gè)TokenTypes列表,其中包含了所有不能被抑制的警告信息没咙。
2. Javadoc Comments(Javadoc注釋:6個(gè))
- Package Javadoc(包注釋)
檢查每個(gè)Java包是否都有Javadoc注釋。默認(rèn)情況下袁梗,它只允許使用一個(gè)package-info.java文件遮怜,但是可以將其配置為允許使用package.html文件锯梁。
如果兩個(gè)文件都存在的話陌凳,就會報(bào)告錯(cuò)誤合敦,因?yàn)镴avadoc工具不允許這種情況發(fā)生保檐。
- Method Javadoc(方法注釋)
檢查方法或構(gòu)造器的Javadoc夜只。默認(rèn)不會檢查未使用的throws扔亥。想要將未聲明的java.lang.RuntimeExceptions也納入文檔砸王,則需要將allowUndeclaredRTE屬性設(shè)置為true谦铃。想要驗(yàn)證的可見范圍是由scope屬性指定的,默認(rèn)值為Scope.PRIVATE撒会。想要驗(yàn)證其他的可見范圍屹培,則需要將scope屬性設(shè)置為相應(yīng)的值褪秀。
如果不想顯示由于參數(shù)或類型參數(shù)沒有在注釋中使用param標(biāo)記進(jìn)行說明而產(chǎn)生的錯(cuò)誤消息媒吗,就需要勾選allowMissingParamTags屬性闸英。如果不想顯示由于聲明了拋出異常但沒有在注釋中使用throws標(biāo)記進(jìn)行說明而產(chǎn)生的錯(cuò)誤消息,就需要勾選allowMissingThrowsTags屬性遇伞。如果不想顯示由于return語句是非void類型但沒有在注釋中使用return標(biāo)記進(jìn)行說明而產(chǎn)生的錯(cuò)誤消息加派,就需要勾選allowMissingReturnTag屬性芍锦。
由@Override注解標(biāo)記的方法次乓,它的Javadoc不是必需的票腰。但是在Java 5下杏慰,無法為接口中的方法加上標(biāo)記(已經(jīng)在Java 6中修正)缘滥。因此朝扼,CheckStyle支持使用單個(gè)的{@inheritDoc}標(biāo)記來代替所有其他的標(biāo)記擎颖。例如,如果下面的方法實(shí)現(xiàn)了接口中的某個(gè)方法备典,那么Javadoc可以這樣編寫:
/** {@inheritDoc} */
public int checkReturnTag(final int aTagIndex,
JavadocTag[] aTags,
int aLineNo)
- Style Javadoc(風(fēng)格注釋)
驗(yàn)證Javadoc注釋,以便于確保它們的格式拌屏∫形梗可以檢查以下注釋:接口聲明焦读、類聲明矗晃、方法聲明张症、構(gòu)造器聲明俗他、變量聲明兆衅。
- Type Javadoc(類型注釋)
檢查方法或構(gòu)造器的Javadoc。默認(rèn)不會檢查author和version標(biāo)記周伦。想要驗(yàn)證的可見范圍是由scope屬性指定的及志,默認(rèn)值為Scope.PRIVATE速侈。想要驗(yàn)證其他的可見范圍倚搬,則需要將scope屬性設(shè)置為相應(yīng)的值每界。想要為author標(biāo)記或version標(biāo)記定義格式眨层,將authorFormat屬性或versionFormat屬性設(shè)置為指定的正則表達(dá)式即可。
如果不想顯示由于類型參數(shù)沒有在注釋中使用param標(biāo)記進(jìn)行說明而產(chǎn)生的錯(cuò)誤消息叁征,就需要勾選allowMissingParamTags屬性航揉。
- Variable Javadoc(變量注釋)
檢查變量是否具有Javadoc注釋帅涂。
- Write Tag(輸出標(biāo)記)
將Javadoc作為信息輸出斯议『哂可以作為樣式表來使用恋昼,根據(jù)作者名排序報(bào)告液肌。想要為一個(gè)標(biāo)記定義格式,將tagFormat屬性設(shè)置為相應(yīng)的正則表達(dá)式即可婿滓。這項(xiàng)檢查會使用兩種不同的嚴(yán)重級別橘券。當(dāng)標(biāo)記缺失時(shí)旁舰,使用標(biāo)準(zhǔn)的嚴(yán)重級別(Severity)鬓梅。當(dāng)標(biāo)記存在時(shí),使用附加的嚴(yán)重級別(tagSeverity)作為報(bào)告等級紧阔。
3. Naming Conventions(命名規(guī)約:12個(gè))
- Abstract Class Name(抽象類名稱)
檢查抽象類的名稱是否遵守命名規(guī)約活孩。
- Class Type Parameter Name(類的類型參數(shù)名稱)
檢查類的類型參數(shù)名稱是否遵守命名規(guī)約憾儒。
- Constant Names(常量名稱)
檢查常量(用static final修飾的字段)的名稱是否遵守命名規(guī)約起趾。
- Local Final Variable Names(局部final變量名稱)
檢查局部final變量的名稱是否遵守命名規(guī)約。
- Local Variable Names(局部變量名稱)
檢查局部變量的名稱是否遵守命名規(guī)約边琉。
- Member Names(成員名稱)
檢查成員變量(非靜態(tài)字段)的名稱是否遵守命名規(guī)約着茸。
- Method Names(方法名稱)
檢查方法名稱是否遵守命名規(guī)約。
- Method Type Parameter Name(方法的類型參數(shù)名稱)
檢查方法的類型參數(shù)名稱是否遵守命名規(guī)約忧额。
- Package Names(包名稱)
檢查包名稱是否遵守命名規(guī)約睦番。
- Parameter Names(參數(shù)名稱)
檢查參數(shù)名稱是否遵守命名規(guī)約托嚣。
- Static Variable Names(靜態(tài)變量名稱)
檢查靜態(tài)變量(用static修飾,但沒用final修飾的字段)的名稱是否遵守命名規(guī)約夫嗓。
- Type Names(類型名稱)
檢查類的名稱是否遵守命名規(guī)約。
4. Headers(文件頭:2個(gè))
- Header(文件頭)
檢查源碼文件是否開始于一個(gè)指定的文件頭矩父。headerFile屬性可以指定一個(gè)文件,該文件包含了需要的文件頭球订。還有另一種方法辙售,文件頭的內(nèi)容可以直接在header屬性中設(shè)置旦部,而不使用外部文件士八。
ignoreLines屬性可以設(shè)置行號,指定檢查時(shí)忽略頭文件中的哪些行蝗茁。如果想要支持包含版權(quán)日期的文件頭寻咒,那么這個(gè)屬性就顯得非常有用饭寺。例如艰匙,考慮下面的文件頭:
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: ////////////////////////////////////////////////////////////////////
因?yàn)槟攴菪畔S著時(shí)間改變员凝,通過將ignoreLines屬性設(shè)置為4硕舆,你就可以告訴CheckStyle忽略第4行。
- Regular Expression Header(正則表達(dá)式文件頭)
檢查Java源碼文件頭部的每行是否匹配指定的正則表達(dá)式阶捆。
解釋:在某些項(xiàng)目中洒试,檢查固定的頭部是不足夠的,例如痪宰,文件頭可能需要一行版權(quán)信息乖订,但是其中的年份信息會隨著時(shí)間變化乍构。
例如哥遮,考慮下面的文件頭:
line 1: ^/{71}$
line 2: ^// checkstyle:$
line 3: ^// Checks Java source code for adherence to a set of rules\.$
line 4: ^// Copyright C
C
\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個(gè)'/'符號。第4行表示版權(quán)信息中的年份的格式是四位數(shù)字君仆。第5行示范了如何在文件頭部添加校正控制關(guān)鍵字返咱。第12-14行是Javadoc的模板(第13行非常復(fù)雜咖摹,它可以抑制Javadoc注釋中的沖突)萤晴。
5. Imports(導(dǎo)入:7個(gè))
- Avoid Star (Demand) Imports(避免通配符導(dǎo)入)
檢查沒有import語句使用*符號嗦枢。
解釋:從一個(gè)包中導(dǎo)入所有的類會導(dǎo)致包之間的緊耦合文虏,當(dāng)一個(gè)新版本的庫引入了命名沖突時(shí),這樣就有可能導(dǎo)致問題發(fā)生丸相。
- Avoid Static Imports(避免靜態(tài)導(dǎo)入)
檢查沒有靜態(tài)導(dǎo)入語句灭忠。
解釋:導(dǎo)入靜態(tài)成員可能會導(dǎo)致類成員之間的命名沖突更舞。導(dǎo)入靜態(tài)成員可能會導(dǎo)致代碼的可讀性很差,因?yàn)樽x者可能會搞不清楚成員到底位于哪個(gè)類中刊头。
- Illegal Imports(非法導(dǎo)入)
檢查是否導(dǎo)入了指定的非法包原杂。默認(rèn)情況下穿肄,這項(xiàng)檢查會拒絕所有的sun.包,因?yàn)橹苯邮褂胹un.包的程序肯定不是100%的純Java程序脑溢。想要拒絕其他的包屑彻,將illegalPkgs屬性設(shè)置為你指定的非法包列表即可社牲。
- Import Order Check(導(dǎo)入順序檢查)
檢查導(dǎo)入包的順序/分組违寿。確保導(dǎo)入包的分組按照指定的順序排列(例如,java.排在首位痛阻,javax.排在第二阱当,以此類推),并且每個(gè)分組內(nèi)導(dǎo)入的包都是按照字典序排列的油坝。靜態(tài)導(dǎo)入必須放在最后澈圈,并且也是按照字典序排列的瞬女。
- Redundant Imports(多余導(dǎo)入)
檢查是否存在多余的導(dǎo)入語句。如果一條導(dǎo)入語句滿足以下條件报慕,那么就是多余的:
它是另一條導(dǎo)入語句的重復(fù)卖子。也就是洋闽,一個(gè)類被導(dǎo)入了多次羽利。
從java.lang包中導(dǎo)入類这弧,例如匾浪,導(dǎo)入java.lang.String。
從當(dāng)前包中導(dǎo)入類冷溶。
- Unused Imports(未使用導(dǎo)入)
檢查未使用的導(dǎo)入語句逞频。CheckStyle使用一種簡單可靠的算法來報(bào)告未使用的導(dǎo)入語句。如果一條導(dǎo)入語句滿足以下條件柒巫,那么就是未使用的:
沒有在文件中引用堡掏。這種算法不支持通配符導(dǎo)入泉唁,例如,java.io.*拴鸵;八堡。大多數(shù)IDE檢查帶有通配符的導(dǎo)入語句時(shí)兄渺,使用的算法非常復(fù)雜挂谍。
它是另一條導(dǎo)入語句的重復(fù)。也就是妄田,一個(gè)類被導(dǎo)入了多次形庭。
從java.lang包中導(dǎo)入類,例如苇倡,導(dǎo)入java.lang.String。
從當(dāng)前包中導(dǎo)入類综慎。
可選:在Javadoc注釋中引用它示惊。這項(xiàng)檢查默認(rèn)是關(guān)閉的米罚,因?yàn)閮H僅為了抽出文檔而引入了一個(gè)編譯時(shí)依賴是一個(gè)很壞的習(xí)慣。例如隘竭,當(dāng)Javadoc注釋中包含{@link Date}時(shí)嗤形,就會認(rèn)為import java.util.Date被引用了赋兵。想要避免引入編譯時(shí)依賴霹期,可把Javadoc注釋寫成{@link java.util.Date}
- Import Control(導(dǎo)入控制)
控制允許導(dǎo)入每個(gè)包中的哪些類历造。可用于確保應(yīng)用程序的分層規(guī)則不會違法臣淤,特別是在大型項(xiàng)目中邑蒋。
導(dǎo)入控制XML文檔所使用的DTD文件位于
http://www.puppycrawl.com/dtds/import_control_1_0.dtd。它包含了上述XML文檔的所有元素和屬性卿堂。
6. Size Violations(尺寸超標(biāo):8個(gè))
- Anonymous inner classes lengths(匿名內(nèi)部類長度)
檢查匿名內(nèi)部類的長度草描。
解釋:如果一個(gè)匿名內(nèi)部類變得非常長,那么就很難讀懂它揍诽,并且難以跟蹤方法的流程。因此渠啤,過長的匿名內(nèi)部類通常應(yīng)當(dāng)被重構(gòu)為一個(gè)具名內(nèi)部類〖嗣溃可以參考Bloch編寫的《Effective Java》的第93頁壶栋。
- Executable Statement Size(可執(zhí)行語句數(shù)量)
將可執(zhí)行語句的數(shù)量限制為一個(gè)指定的限值。
- Maximum File Length(最大文件長度)
檢查源碼文件的長度毙玻。
解釋:如果一個(gè)源碼文件變得非常長桑滩,那么就會難以理解施符。因此,過長的類通常應(yīng)當(dāng)被重構(gòu)成若干個(gè)較小的獨(dú)立的類贯涎,每個(gè)類專注于一個(gè)特定的任務(wù)。
- Maximum Line Length(最大行長度)
檢查源碼每行的長度败明。
解釋:將源碼打印出來妻顶,或者開發(fā)者限制了屏幕顯示源碼的空間(例如幔嗦,IDE會顯示額外的信息,諸如項(xiàng)目樹汇恤、類層次等等)仁连,那么過長的行會顯得難以閱讀使鹅。
- Maximum Method Length(最大方法長度)
檢查方法和構(gòu)造器的長度患朱。
解釋:如果一個(gè)方法變得非常長,那么就會難以理解执虹。因此,過長的方法通常應(yīng)當(dāng)被重構(gòu)成若干個(gè)較小的獨(dú)立的方法当叭,每個(gè)方法專注于一個(gè)特定的任務(wù)磺芭。
- Maximum Parameters(最大參數(shù)數(shù)量)
檢查一個(gè)方法或構(gòu)造器的參數(shù)的數(shù)量钾腺。
- Outer Type Number(外層類型數(shù)量)
檢查在一個(gè)文件的外層(或根層)中聲明的類型的數(shù)量松邪。
解釋:一般認(rèn)為給每個(gè)文件只定義一個(gè)外層類型是較好的做法逗抑。
- Method Count(方法總數(shù))
檢查每個(gè)類型中聲明的方法的數(shù)量。
它包括了每種可見范圍方法的總數(shù)(private褂傀、package鳄梅、protected叠国、public)。
7. Whitespace(空格:12個(gè))
- Generic Whitespace(范型標(biāo)記空格)
檢查范型標(biāo)記<和>的周圍的空格是否遵守標(biāo)準(zhǔn)規(guī)約戴尸。這項(xiàng)規(guī)約是不可配置的粟焊。
例如,以下代碼都是合法的:
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循環(huán)初始化語句的填充符孙蒙,也就是空格是否可以作為for循環(huán)初始化語句空位置的填充符香追。如果代碼自動(dòng)換行迁央,則不會進(jìn)行檢查,正如以下代碼所示:
for (; i < j; i++, j--)
- Empty For Iterator Pad(空白for迭代器填充符)
檢查空的for循環(huán)迭代器的填充符顽决,也就是空格是否可以作為for循環(huán)迭代器空位置的填充符赋访。如果代碼自動(dòng)換行旋炒,則不會進(jìn)行檢查,正如以下代碼所示:
for (Iterator foo = very.long.line.iterator();
foo.hasNext();
)
- No Whitespace After(指定標(biāo)記之后沒有空格)
檢查指定標(biāo)記之后沒有空格。若要禁用指定標(biāo)記之后的換行符,將allowLineBreaks屬性設(shè)為false即可。
- No Whitespace Before(指定標(biāo)記之前沒有空格)
檢查指定標(biāo)記之前沒有空格。若要允許指定標(biāo)記之前的換行符,將allowLineBreaks屬性設(shè)為true即可。
- Operator Wrap(運(yùn)算符換行)
檢查代碼自動(dòng)換行時(shí)灯荧,運(yùn)算符所處位置的策略。nl表示運(yùn)算符必須在新行中,eol表示運(yùn)算符必須在當(dāng)前行的行末。
- Method Parameter Pad(方法參數(shù)填充符)
檢查方法定義、構(gòu)造器定義、方法調(diào)用、構(gòu)造器調(diào)用的標(biāo)識符和參數(shù)列表的左圓括號之間的填充符。也就是,如果標(biāo)識符和左圓括號位于同一行,那么就檢查標(biāo)識符之后是否需要緊跟一個(gè)空格。如果標(biāo)識符和左圓括號不在同一行蔬墩,那么就報(bào)錯(cuò)蔬蕊,除非將規(guī)則配置為允許使用換行符猜扮。想要在標(biāo)識符之后使用換行符煮盼,將allowLineBreaks屬性設(shè)置為true即可报破。
- Paren Pad(圓括號填充符)
檢查圓括號的填充符策略盹靴,也就是在左圓括號之后和右圓括號之前是否需要有一個(gè)空格踪宠。
- Typecast Paren Pad(類型轉(zhuǎn)換圓括號填充符)
檢查類型轉(zhuǎn)換的圓括號的填充符策略柬脸。也就是垦巴,在左圓括號之后和右圓括號之前是否需要有一個(gè)空格。
- File Tab Character(文件制表符)
檢查源碼中沒有制表符('\t')望门。
解釋:
為了能夠更方便地閱讀源碼纫事,開發(fā)者不應(yīng)當(dāng)在他們的文本編輯器中配置制表符的寬度万哪。
根據(jù)Apache Jakarta的編碼標(biāo)準(zhǔn):在一個(gè)分布式開發(fā)環(huán)境中的止,當(dāng)提交的消息被發(fā)送到一個(gè)郵件列表中時(shí),如果你使用了制表符,那么這些消息會變得幾乎不可能閱讀。
- Whitespace After(指定標(biāo)記之后有空格)
檢查指定標(biāo)記之后是否緊跟了空格砸民。
- Whitespace Around(指定標(biāo)記周圍有空格)
檢查指定標(biāo)記的周圍是否有空格姿染。以下形式的空構(gòu)造器和方法的代碼體(代碼塊):
public MyClass() {} // 空構(gòu)造器
public void func() {} // 空方法
可以選擇性地從檢查策略中排除闽颇,通過設(shè)置allowEmptyMethods和allowEmptyConstructors屬性即可。
8. Regexp(正則表達(dá)式:3個(gè))
- RegexpSingleline(正則表達(dá)式單行匹配)
檢查單行是否匹配一條給定的正則表達(dá)式奈懒≤罘幔可以處理任何文件類型汤功。
解釋:這項(xiàng)檢查可以作為原型檢查使用,能夠發(fā)現(xiàn)常見的編碼壞習(xí)慣蝌矛,例如調(diào)用ex.printStacktrace()噪伊、System.out.println()豆励、System.exit(),等等。
- RegexpMultiline(正則表達(dá)式多行匹配)
檢查多行是否匹配一條給定的正則表達(dá)式祷蝌∩星ⅲ可以處理任何文件類型睛挚。
解釋:這項(xiàng)檢查可以作為原型檢查使用,能夠發(fā)現(xiàn)常見的編碼壞習(xí)慣印机,例如調(diào)用ex.printStacktrace()、System.out.println()、System.exit()漫贞,等等。
- RegexpSingleLineJava(正則表達(dá)式單行Java匹配)
這項(xiàng)檢查是RegexpSingleline的變種钦睡,用于檢測Java文件中的單行是否匹配給定的正則表達(dá)式。它支持通過Java注釋抑制匹配操作完丽。
9. Modifiers(修飾符:2個(gè))
- Modifier Order(修飾符順序)
檢查代碼中的標(biāo)識符的順序是否符合《Java Language Specification》中的第8.1.1、8.3.1章節(jié)所建議的順序窿春。正確的順序應(yīng)當(dāng)如下:
- public
- protected
- private
- abstract
- static
- final
- transient
- volatile
- synchronized
- native
- strictfp
- Redundant Modifier(多余修飾符)
在以下部分檢查是否有多余的修飾符:
接口和注解的定義烦租;
final類的方法的final修飾符;
被聲明為static的內(nèi)部接口聲明泻仙。
解釋:《Java Language Specification》強(qiáng)烈不建議在接口定義中使用“public”和“abstract”來聲明方法刺下。
接口中的變量和注解默認(rèn)就是public、static箱歧、final的,因此濒翻,這些修飾符也是多余的肌稻。
因?yàn)樽⒔馐墙涌诘囊环N形式救斑,所以它們的字段默認(rèn)也是public嫁盲、static、final的幸斥,正如它們的注解字段默認(rèn)是public和abstract的堡妒。
定義為final的類是不能被繼承的燥透,因此,final類的方法的final修飾符也是多余的。
10. Blocks(代碼塊:5個(gè))
- Avoid Nested Blocks(避免嵌套代碼塊)
找到嵌套代碼塊积担,也就是在代碼中無節(jié)制使用的代碼塊苇瓣。
解釋:內(nèi)嵌代碼塊通常是調(diào)試過程的殘留物,它們會使讀者產(chǎn)生混淆砾肺。
- Empty Block(空代碼塊)
檢查空代碼塊。
- Left Curly Brace Placement(左花括號位置)
檢查代碼塊的左花括號的放置位置缀遍。
通過property選項(xiàng)指定驗(yàn)證策略屉凯。
若使用eol和nlow策略年叮,則需要考慮maxLineLength屬性。
- Need Braces(需要花括號)
檢查代碼塊周圍是否有大括號苞笨,可以檢查do、else冠跷、if、for隧枫、while等關(guān)鍵字所控制的代碼塊喉磁。
- Right Curly Brace Placement(右花括號位置)
檢查else、try官脓、catch標(biāo)記的代碼塊的右花括號的放置位置协怒。
通過property選項(xiàng)指定驗(yàn)證策略。
11. Coding Problems(編碼問題:43個(gè))
- Avoid Inline Conditionals(避免內(nèi)聯(lián)條件語句)
檢測內(nèi)聯(lián)條件語句确买。內(nèi)聯(lián)條件語句的一個(gè)示例如下所示:
String a = getParameter("a");
String b = (a==null || a.length<1) ? null : a.substring(1);
解釋:有些開發(fā)者發(fā)現(xiàn)內(nèi)聯(lián)條件語句很難讀懂斤讥,因此他們公司的編碼標(biāo)準(zhǔn)會禁止使用內(nèi)聯(lián)條件語句。
- Covariant Equals(共變equals方法)
檢查定義了共變equals()方法的類中是否同樣覆蓋了equals(java.lang.Object)方法湾趾。這項(xiàng)檢查受到FindBugs的啟發(fā)芭商。
解釋:錯(cuò)誤地定義了一個(gè)共變equals()方法,而沒有覆蓋equals(java.lang.Object)方法搀缠,可能會產(chǎn)生不可預(yù)料的運(yùn)行時(shí)行為铛楣。
- Default Comes Last(默認(rèn)分支置于最后)
檢查switch語句中的default是否在所有的case分支之后。
解釋:Java允許default位于switch語句中的任何地方艺普。但是簸州,如果default位于最后一個(gè)case分支之后鉴竭,那么代碼的可讀性會更強(qiáng)。
- Declaration Order Check(聲明順序檢查)
根據(jù)Java編程語言的編碼規(guī)約岸浑,一個(gè)類或接口的聲明部分應(yīng)當(dāng)按照以下順序出現(xiàn):
類(靜態(tài))變量搏存。首先應(yīng)當(dāng)是public類變量,然后是protected類變量矢洲,然后是package類變量(沒有訪問標(biāo)識符)璧眠,最后是private類變量。
實(shí)例變量读虏。首先應(yīng)當(dāng)是public類變量责静,然后是protected類變量,然后是package類變量(沒有訪問標(biāo)識符)盖桥,最后是private類變量灾螃。
構(gòu)造器
方法
- Empty Statement(空語句)
檢測代碼中是否有空語句(也就是單獨(dú)的;符號)。
- Equals Avoid Null(避免調(diào)用空引用的equals方法)
檢查equals()比較方法中揩徊,任意組合的String常量是否位于左邊腰鬼。
這項(xiàng)檢查還會處理String.equalsIgnoreCase()調(diào)用(可以抑制這種警告)。
解釋:調(diào)用String常量的equals()方法可以避免潛在的NullPointerException靴拱。同樣垃喊,經(jīng)常會發(fā)現(xiàn)在調(diào)用equals()方法之前,會進(jìn)行空指針檢查袜炕,不過在下面的示例中則沒有必要這么做本谜。
例如:
String nullString = null;
nullString.equals("My_Sweet_String");
這段代碼應(yīng)當(dāng)重構(gòu)為:
String nullString = null;
"My_Sweet_String".equals(nullString);
局限:如果覆蓋了equals方法,或者定義了一個(gè)共變equals方法偎窘,并且沒有正確地實(shí)現(xiàn)這個(gè)方法(也就是s.equals(t)返回的結(jié)果和t.equals(s)返回的結(jié)果不同)乌助,那么改寫調(diào)用方法的對象和參數(shù)可能會產(chǎn)生無法預(yù)料的結(jié)果。
Java的Autoboxing特性會對這項(xiàng)檢查如何實(shí)現(xiàn)產(chǎn)生影響陌知。在Java 5之前的版本他托,所有的IDENT + IDENT對象拼接不會導(dǎo)致NullPointerException,即使它是空指針仆葡。這項(xiàng)檢查已經(jīng)包含了這些情況赏参。它們會進(jìn)行簡單的處理,就好像使用String.valueof()方法包圍起來一樣沿盅,這個(gè)方法會拼接null字符串把篓。
以下示例將會導(dǎo)致一個(gè)NullPointerException,這是Autoboxing功能所造成的結(jié)果:
Integer i = null, j = null;
String number = "5"
number.equals(i + j);
因?yàn)楹茈y確定正在拼接的是哪種類型的對象腰涧,所以所有的IDENT拼接都會被認(rèn)為是不安全的韧掩。
- Equals and HashCode(equals方法和hashCode方法)
檢查覆蓋了equals()方法的類是否也覆蓋了hashCode()方法。
解釋:equals()方法和hashCode()方法約定窖铡,相等的對象必然具有相同的哈希碼疗锐。因此坊谁,只要你覆蓋了equals()方法,你就必須同時(shí)覆蓋hashCode()方法滑臊,以確笨谏郑可以在基于哈希的集合中使用你的類。
- Explicit Initialization(顯式初始化)
檢查類或?qū)ο蟮某蓡T是否顯式地初始化為成員所屬類型的默認(rèn)值(對象引用的默認(rèn)值為null简珠,數(shù)值和字符類型的默認(rèn)值為0阶界,布爾類型的默認(rèn)值為false)。
解釋:每個(gè)實(shí)例變量都會被初始化兩次聋庵,并且初始化為相同的值。在執(zhí)行代碼中指定的任何初始化操作之前芙粱,Java會初始化每個(gè)實(shí)例變量為它的默認(rèn)值(0或null)祭玉。因此在這種情況下,x會被初始化為0兩次春畔,bar會被初始化為null兩次脱货。因此,這樣稍微有些效率低下律姨。這種編碼風(fēng)格是C/C++編碼風(fēng)格的延續(xù)振峻,它表明開發(fā)者并不是真正有把握J(rèn)ava能夠初始化實(shí)例變量為它的默認(rèn)值。
- Fall Through(跨越分支)
檢查switch語句中是否存在跨越分支择份。如果一個(gè)case分支的代碼中缺少break扣孟、return、throw或continue語句荣赶,那么就會導(dǎo)致跨越分支凤价。
這項(xiàng)檢查可以通過特殊的注釋以抑制警告。默認(rèn)情況下拔创,在有跨越分支的case分支代碼中添加“fallthru”利诺、“fall through”、“fallthrough”剩燥、“falls through”慢逾、“fallsthrough”等注釋(區(qū)分大小寫)時(shí),便可抑制警告灭红。包含以上單詞的注釋必須在一行中侣滩,并且必須在當(dāng)前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;
}
注意:這項(xiàng)檢查假設(shè)case分支代碼中沒有不可達(dá)的代碼胜卤。
- Final Local Variable(final局部變量)
檢查從未改變?nèi)≈档木植孔兞渴欠癖宦暶鳛閒inal。這項(xiàng)檢查還可以被配置為檢查未修改過的參數(shù)是否被聲明為final赁项。
當(dāng)配置為檢查參數(shù)時(shí)葛躏,這項(xiàng)檢查會忽略接口方法和抽象方法中的參數(shù)澈段。
- Hidden Field(隱藏字段)
檢查局部變量或參數(shù)是否會遮蔽在相同類中定義的字段。
- Illegal Instantiation(非法實(shí)例化)
檢查是否有不合法的實(shí)例化操作舰攒,是否使用工廠方法更好败富。
解釋:根據(jù)不同的項(xiàng)目,對于某些類來說摩窃,可能通過工廠方法來創(chuàng)建類實(shí)例更好兽叮,而不是調(diào)用類構(gòu)造器。
一個(gè)簡單的示例就是java.lang.Boolean類猾愿。為了節(jié)省內(nèi)存和CPU周期鹦聪,最好使用預(yù)定義的常量TRUE和FALSE。構(gòu)造器的調(diào)用應(yīng)當(dāng)被替換為調(diào)用Boolean.valueOf()方法蒂秘。
某些對性能有極端要求的項(xiàng)目可能需要其他的類也使用工廠方法泽本,以便于提高緩存或?qū)ο蟪氐氖褂眯省?/p>
- Illegal Catch(非法異常捕捉)
從不允許捕捉j(luò)ava.lang.Exception、java.lang.Error姻僧、java.lang.RuntimeException的行為规丽。
解釋:缺乏經(jīng)驗(yàn)的開發(fā)者經(jīng)常會簡單地捕捉Exception異常,試圖處理多種異常類型撇贺。這會很不幸地使代碼無意中捕捉到NullPointerException赌莺、OutOfMemoryErrors等系統(tǒng)異常。
- Illegal Throws(非法異常拋出)
這項(xiàng)檢查可以用來確保類型不能聲明拋出指定的異常類型松嘶。從不允許聲明拋出java.lang.Error或java.lang.RuntimeException艘狭。
- Illegal Tokens(非法標(biāo)記)
檢查不合法的標(biāo)記。
解釋:某個(gè)語言特性經(jīng)常會導(dǎo)致代碼難以維護(hù)喘蟆,或者開發(fā)新手難以理解缓升。在某些框架中,其他特性可能不推薦使用蕴轨,例如港谊,在EJB組件中最好不要使用本地方法。
- Illegal Tokens Text(非法標(biāo)記文本)
檢查是否有不合法的標(biāo)記文本橙弱。
- Illegal Type(非法類型)
檢查代碼中是否有在變量聲明歧寺、返回值、參數(shù)中都沒有作為類型使用過的特定類棘脐。包括一種格式檢查功能斜筐,默認(rèn)情況下不允許抽象類。
解釋:幫助減少和實(shí)體類之間的耦合蛀缝。另外顷链,抽象類應(yīng)當(dāng)被認(rèn)為是接口的一種簡便的基類實(shí)現(xiàn),因此不能是類型本身屈梁。
- Inner Assignment(內(nèi)部賦值)
檢查子表達(dá)式中是否有賦值語句嗤练,例如String s = Integer.toString(i = 2);榛了。
解釋:這項(xiàng)檢查會忽略for循環(huán)代碼,其余所有的賦值操作都應(yīng)當(dāng)在它們自己的頂層語句中煞抬,以便于增強(qiáng)可讀性霜大。在上述的內(nèi)部賦值代碼中,很難看到變量是在哪兒賦值的革答。
- JUnit Test Case(JUnit測試用例)
確保setUp()战坤、tearDown()方法的名稱正確,沒有任何參數(shù)残拐,返回類型為void途茫,是public或protected的。
同樣確保suite()方法的名稱正確溪食,沒有參數(shù)慈省,返回類型為junit.framewotk.Test,并且是public和static的眠菇。
解釋:開發(fā)者時(shí)常會錯(cuò)誤地命名這些方法,并且不會意識到這些方法沒有被調(diào)用袱衷。
- Magic Number(幻數(shù))
檢查代碼中是否含有“幻數(shù)”捎废,幻數(shù)就是沒有被定義為常量的數(shù)值文字。默認(rèn)情況下致燥,-1登疗、0、1嫌蚤、2不會被認(rèn)為是幻數(shù)辐益。
- Missing Constructor(缺少構(gòu)造器)
檢查類(除了抽象類)是否定義了一個(gè)構(gòu)造器,而不是依賴于默認(rèn)構(gòu)造器脱吱。
- Missing Switch Default(缺少switch默認(rèn)分支)
檢查switch語句是否含有default子句智政。
解釋:在每個(gè)switch語句中引入一條默認(rèn)分支通常是一個(gè)很好的主意。即使開發(fā)者確信所有當(dāng)前可能的分支都能覆蓋到箱蝠,這也應(yīng)當(dāng)在default分支中表達(dá)出來续捂,例如,使用一條斷言宦搬。這種方法使得代碼可以應(yīng)付以后的修改牙瓢,例如,在一個(gè)枚舉類型中引入新的類型间校。
- Modified Control Variable(修改控制變量)
檢查確保for循環(huán)的控制變量沒有在for代碼塊中被修改矾克。示例代碼如下:
for (int i = 0; i < 1; i++) {
i++;
}
解釋:如果在循環(huán)體中修改了控制變量,程序流程就會變得更加難以跟蹤憔足⌒哺剑可以用while循環(huán)替換for循環(huán)酒繁。
- Multiple String Literals(多重字符串常量)
檢查在單個(gè)文件中,相同的字符串常量是否出現(xiàn)了多次汉嗽。
解釋:重復(fù)代碼會使得維護(hù)工作變得更加困難欲逃,因此最好用一個(gè)常量來替換多次出現(xiàn)。
- Multiple Variable Declaration(多重變量聲明)
檢查每個(gè)變量是否使用一行一條語句進(jìn)行聲明饼暑。
解釋:《SUN編碼規(guī)約》的第6.1章節(jié)推薦應(yīng)當(dāng)使用一行一條語句聲明一個(gè)變量稳析。
- Nested For Depth(for嵌套深度)
限制for循環(huán)的嵌套層數(shù)(默認(rèn)值為1)。
- Nested If Depth(if嵌套深度)
限制if-else代碼塊的嵌套層數(shù)(默認(rèn)值為1)弓叛。
- Nested Try Depth(try嵌套深度)
限制try代碼塊的嵌套層數(shù)(默認(rèn)值為1)彰居。
- No Clone(沒有clone方法)
檢查是否覆蓋了Object類中的clone()方法。
解釋:clone()方法依賴于一套奇怪且難以遵循的規(guī)則撰筷,這套規(guī)則并不是在所有情況下都起作用陈惰。因此,很難正確地覆蓋clone()方法毕籽。下面是一些說明為何應(yīng)當(dāng)避免使用clone()方法的原因抬闯。
支持clone方法的類應(yīng)當(dāng)事先Cloneable接口,但是Cloneable結(jié)構(gòu)并不包含clone方法关筒。因此溶握,它并不會強(qiáng)制覆蓋clone方法。
Cloneable接口會強(qiáng)迫對象的clone方法正確地工作蒸播。如果不實(shí)現(xiàn)這個(gè)接口睡榆,那么對象的clone方法會拋出CloneNotSupportedException。沒有用final關(guān)鍵字修飾的類必須返回由調(diào)用super.clone()方法所返回的對象袍榆。用final關(guān)鍵字修飾的類可以使用一個(gè)構(gòu)造器創(chuàng)建一個(gè)克隆對象胀屿,這個(gè)對象和非final類的有所不同。
如果一個(gè)父類沒有正確地實(shí)現(xiàn)clone方法包雀,那么所有的子類調(diào)用super.clone()方法時(shí)宿崭,都會注定失敗。
如果一個(gè)類含有可變對象的引用馏艾,那么在這個(gè)類的clone方法中調(diào)用super.clone()方法之后劳曹,必須使用前述可變對象的拷貝來替換這些對象引用。
clone方法不能正確地處理用final關(guān)鍵字修飾的可變對象引用琅摩,因?yàn)閒inal引用不能被重新賦值铁孵。
如果一個(gè)父類覆蓋了clone方法,那么所有的子類都必須提供一個(gè)正確的clone實(shí)現(xiàn)房资。
在某些情況下蜕劝,有兩種clone方法的替代方案可以使用,一種是使用一個(gè)拷貝構(gòu)造器,另一種是使用靜態(tài)工廠方法來返回某個(gè)對象的拷貝岖沛。這兩種方法都更加簡單暑始,并且不會和final關(guān)鍵字修飾的字段產(chǎn)生沖突。它們不會強(qiáng)迫調(diào)用的客戶端處理CloneNotSupportException婴削。它們都是具有類型的廊镜,因此不需要進(jìn)行任何類型轉(zhuǎn)換。最后唉俗,它們更加靈活嗤朴,因?yàn)樗鼈兛梢蕴幚斫涌陬愋停粌H僅是實(shí)體類虫溜。
有時(shí)候雹姊,不能使用拷貝構(gòu)造器或靜態(tài)工廠作為clone方法的替代方案。以下示例說明了拷貝構(gòu)造器或靜態(tài)工廠的局限性衡楞。假設(shè)Square是Shape的一個(gè)子類吱雏。
Shape s1 = new Square();
System.out.println(s1 instanceof Square); //true
...假設(shè)此處的代碼不知道s1是一個(gè)Square類型的對象硫眨,這是多態(tài)的優(yōu)美之處缘挑,但是代碼想要拷貝這個(gè)被聲明為Shape類型的Square對象低零,Shape是Square的父類...
Shape s2 = new Shape(s1); //using the copy constructor
System.out.println(s2 instanceof Square); //false
有效的解決辦法(不用知道所有的子類罚渐,并且不用執(zhí)行大量的類型轉(zhuǎn)換)應(yīng)當(dāng)如下列代碼所示(假設(shè)實(shí)現(xiàn)了正確的clone方法):
Shape s2 = s1.clone();
System.out.println(s2 instanceof Square); //true
你只需要記住,如果需要這種多態(tài)克隆的類型莱坎,那么一個(gè)正確實(shí)現(xiàn)的clone方法可能才是最好的選擇盐肃。
這項(xiàng)檢查和{@link NoFinalizerCheck}幾乎完全相同俯艰。
- No Finalizer(沒有finalize方法)
驗(yàn)證類中是否定義了finalize()方法盒犹。
- Package Declaration(包聲明)
確保一個(gè)類具有一個(gè)包聲明,并且(可選地)包名要與源代碼文件所在的目錄名相匹配眨业。
解釋:位于空包中的類是不能夠被導(dǎo)入的急膀。很多開發(fā)新手并沒有注意到這一點(diǎn)。
- Parameter Assignment(參數(shù)賦值)
不允許對參數(shù)進(jìn)行賦值龄捡。
解釋:對參數(shù)的賦值通常被認(rèn)為是缺乏編程實(shí)踐經(jīng)驗(yàn)卓嫂。強(qiáng)迫開發(fā)者將參數(shù)聲明為final通常是非常麻煩的。這項(xiàng)檢查可以確保參數(shù)從不會被賦值聘殖,這對于雙方都是好事晨雳。
- Redundant Throws(多余的throws)
檢查throws子句中是否聲明了多余的異常,例如重復(fù)異常奸腺、未檢查的異巢徒或一個(gè)已聲明拋出的異常的子類。
- Require This(需要this)
檢查代碼是否使用了“this.”突照,也就是說帮非,在默認(rèn)情況下,引用當(dāng)前對象的實(shí)例變量和方法時(shí),應(yīng)當(dāng)顯式地通過“this.varName”或“this.methodName(args)”這種形式進(jìn)行調(diào)用末盔。
- Return Count(return總數(shù))
限制return語句的數(shù)量筑舅。默認(rèn)值為2≡刹眨可以忽略檢查指定的方法(默認(rèn)忽略equals()方法)翠拣。
解釋:過多的返回點(diǎn)可能表明代碼嘗試處理過多的業(yè)務(wù),可能會難以理解游盲。
- Simplify Boolean Expression(簡化布爾表達(dá)式)
檢查是否有過于復(fù)雜的布爾表達(dá)式∥竽梗現(xiàn)在能夠發(fā)現(xiàn)諸如if (b == true)、b || true背桐、!false等類型的代碼优烧。
解釋:復(fù)雜的布爾邏輯會使得代碼難以理解和維護(hù)。
- Simplify Boolean Return(簡化布爾返回值)
檢查是否有過于復(fù)雜的布爾類型return語句链峭。例如下面的代碼:
if (valid())
return false;
else
return true;
可以寫成:
return !valid();
這項(xiàng)檢查是從PMD規(guī)則中借鑒而來的畦娄。
- String Literal Equality(嚴(yán)格的常量等式比較)
檢查字符串對象的比較是否使用了==或!=運(yùn)算符。
解釋:Java新手程序員經(jīng)常會使用類似于下面的代碼:
if (x == "something")
其實(shí)他們是想表達(dá)如下的意思:
if ("something".equals(x))
- SuperClone(父類clone方法)
檢查一個(gè)覆蓋的clone()方法是否調(diào)用了super.clone()方法弊仪。
參考:Object.clone()熙卡。
- SuperFinalize(父類finalize方法)
檢查一個(gè)覆蓋的finalize()方法是否調(diào)用了super.finalize()方法。
參考:清理未使用對象励饵。
- Trailing Array Comma(數(shù)組尾隨逗號)
檢查數(shù)組的初始化是否包含一個(gè)尾隨逗號驳癌。
int[] a = new int[]
{
1,
2,
3,
};
如果左花括號和右花括號都位于同一行,那么這項(xiàng)檢查允許不添加尾隨逗號役听。如下所示:
return new int[] { 0 };
解釋:添加尾隨逗號可以使得改變元素順序颓鲜,或者在末尾添加新的元素變得更加方便。
- Unnecessary Parentheses(不必要的圓括號)
檢查代碼中是否使用了不必要的圓括號典予。
- One Statement Per Line(每行一條語句)
檢查每行是否只有一條語句甜滨。下面的一行將會被標(biāo)識為出錯(cuò):
x = 1; y = 2; // 一行中有兩條語句
12. Class Design(類設(shè)計(jì):8個(gè))
- Designed For Extension(設(shè)計(jì)擴(kuò)展性)
檢查類是否具有可擴(kuò)展性。更準(zhǔn)確地說瘤袖,它強(qiáng)制使用一種編程風(fēng)格衣摩,父類必須提供空的“句柄”,以便于子類實(shí)現(xiàn)它們捂敌。
確切的規(guī)則是艾扮,類中可以由子類繼承的非私有、非靜態(tài)方法必須是:
abstract方法占婉,或
final方法泡嘴,或
有一個(gè)空的實(shí)現(xiàn)
解釋:這種API設(shè)計(jì)風(fēng)格可以保護(hù)父類不會被子類破壞。不利之處在于子類的靈活性會受到限制逆济,特別是它們不能夠阻止父類代碼的執(zhí)行磕诊,但是這也意味著子類不會由于忘記調(diào)用父類的方法而破壞父類的狀態(tài)填物。(個(gè)人理解:不允許類的方法被子類覆蓋)
- Final Class(final類)
檢查一個(gè)只有私有構(gòu)造器的類是否被聲明為final。
- Inner Type Last(最后聲明內(nèi)部類型)
檢查嵌套/內(nèi)部的類型是否在當(dāng)前類的最底部聲明(在所有的方法/字段的聲明之后)霎终。
- Hide Utility Class Constructor(隱藏工具類構(gòu)造器)
確保工具類(在API中只有靜態(tài)方法和字段的類)沒有任何公有構(gòu)造器滞磺。
解釋:實(shí)例化工具類沒有任何意義。因此莱褒,工具類的構(gòu)造器應(yīng)當(dāng)是私有的或者受保護(hù)的(如果你打算以后擴(kuò)展子類)击困。一個(gè)常見的錯(cuò)誤便是忘記隱藏默認(rèn)構(gòu)造器。
如果你打算將工具類的構(gòu)造器聲明為受保護(hù)的广凸,那么你可以考慮下面的構(gòu)造器實(shí)現(xiàn)技術(shù)阅茶,借此可以禁止子類的實(shí)例化:
public class StringUtils // 不是final類,允許子類繼承
{
protected StringUtils() {
throw new UnsupportedOperationException(); // 防止子類調(diào)用
}
public static int count(char c, String s) {
// ...
}
}
- Interface Is Type(接口是類型)
Bloch編寫的《Effective Java》中提到谅海,接口應(yīng)當(dāng)描述為一個(gè)類型脸哀。因此,定義一個(gè)只包含常量扭吁,但是沒有包含任何方法的接口是不合適的撞蜂。標(biāo)準(zhǔn)類javax.swing.SwingConstants是一個(gè)會被這項(xiàng)檢查標(biāo)記的示例類。
這項(xiàng)檢查還可以配置為禁用標(biāo)記接口侥袜,例如java.io.Serializable蝌诡,這種接口不會包含任何方法或常量。
- Mutable Exception(可變異常)
確保異常(異常類的名稱必須匹配指定的正則表達(dá)式)是不可變的枫吧。也就是說浦旱,異常只能有final字段。
這項(xiàng)檢查當(dāng)前使用的算法非常簡單九杂,它會檢查異常的所有成員是否是final的颁湖。用戶仍然可以修改一個(gè)異常實(shí)例(例如,Throwable使用setStackTrace(StackTraceElement[] stackTrace)方法修改堆棧跟蹤)例隆。但是爷狈,至少這種異常類型所提供的信息是不可修改的。
解釋:異常實(shí)例應(yīng)當(dāng)表示一個(gè)錯(cuò)誤狀態(tài)裳擎。異常類中含有非final的字段,不僅僅會導(dǎo)致異常狀態(tài)會由于偶然的因素被修改思币,這樣便會遮蔽原始的異常狀態(tài)鹿响,還會使得開發(fā)者偶爾會忘記初始化異常狀態(tài),這樣便會導(dǎo)致代碼捕捉到異常之后谷饿,根據(jù)異常狀態(tài)推導(dǎo)出不正確的結(jié)論惶我。
- Throws Count(拋出計(jì)數(shù))
將異常拋出語句的數(shù)量配置為一個(gè)指定的限值(默認(rèn)值為1)。
解釋:異常是方法接口的組成部分之一博投。如果一個(gè)方法聲明拋出過多不同的異常绸贡,就會使得異常處理非常繁重,并且會導(dǎo)致不好的編程習(xí)慣,例如catch (Exception)听怕。這項(xiàng)檢查會強(qiáng)制開發(fā)者將異常處理變得具有層次性捧挺,舉個(gè)最簡單的例子,調(diào)用者只需要檢查一種類型的異常尿瞭,但是必要時(shí)也允許捕捉上述異常的任何子類闽烙。
- Visibility Modifier(可見性標(biāo)識符)
檢查類成員的可見性。只有static final的類成員可以是公有的声搁,其他的類成員必須是私有的黑竞,除非設(shè)置了protectedAllowed屬性或packageAllowed屬性。
如果類成員的名稱和指定的公有成員正則表達(dá)式匹配疏旨,那么這項(xiàng)檢查就不會標(biāo)記這個(gè)類成員(默認(rèn)包含“^serialVersionUID$”)很魂。
解釋:強(qiáng)制封裝。
13. Duplicates(重復(fù):1個(gè))
- Strict Duplicate Code(嚴(yán)格重復(fù)代碼)
逐行地比較所有的代碼行檐涝,如果有若干行只有縮進(jìn)有所不同遏匆,那么就報(bào)告存在重復(fù)代碼。Java代碼中的所有的import語句都會被忽略骤铃,任何其他的行 —— 包括Javadoc拉岁、方法之間的空白行,等等 —— 都會被檢查(這也是為什么這項(xiàng)檢查被稱作是嚴(yán)格的)惰爬。
14. Metrics(度量:6個(gè))
- Boolean Expression Complexity(布爾表達(dá)式復(fù)雜度)
限制一個(gè)表達(dá)式中的&&喊暖、||、&撕瞧、|陵叽、^等邏輯運(yùn)算符的數(shù)量。
解釋:過多的條件會導(dǎo)致代碼難以讀懂丛版、調(diào)試和維護(hù)巩掺。
注意,&和|運(yùn)算符并不僅僅是整數(shù)的位運(yùn)算符页畦,它們還是布爾運(yùn)算符&&和||的非快捷版本胖替。
- Class Data Abstraction Coupling(類的數(shù)據(jù)抽象耦合)
這項(xiàng)度量會測量給定類中的其他類的實(shí)例化操作的次數(shù)。這種類型的耦合并不是由于繼承或者面向?qū)ο蠓缎投a(chǎn)生的豫缨。一般而言独令,任何將其他抽象數(shù)據(jù)類型作為成員的抽象數(shù)據(jù)類型都具有數(shù)據(jù)抽象耦合;因此好芭,如果一個(gè)類中的某個(gè)局部變量是另一個(gè)類的實(shí)例(對象)燃箭,那么就存在數(shù)據(jù)抽象耦合(DAC)。DAC越高舍败,系統(tǒng)的數(shù)據(jù)結(jié)構(gòu)(類)就會越復(fù)雜招狸。
- Class Fan Out Complexity(類的扇出復(fù)雜度)
一個(gè)給定類所依賴的其他類的數(shù)量敬拓。這個(gè)數(shù)量的平方還可以用于表示函數(shù)式程序(基于文件)中需要維護(hù)總量的最小值。
- Cyclomatic Complexity(循環(huán)復(fù)雜度)
檢查循環(huán)復(fù)雜度是否超出了指定的限值裙戏。該復(fù)雜度由構(gòu)造器乘凸、方法、靜態(tài)初始化程序挽懦、實(shí)例初始化程序中的if翰意、while、do信柿、for冀偶、?:、catch渔嚷、switch进鸠、case等語句,以及&&和||運(yùn)算符的數(shù)量所測量形病。它是遍歷代碼的可能路徑的一個(gè)最小數(shù)量測量客年,因此也是需要的測試用例的數(shù)量。通常1-4是很好的結(jié)果漠吻,5-7較好量瓜,8-10就需要考慮重構(gòu)代碼了,如果大于11途乃,則需要馬上重構(gòu)代碼绍傲!
- Non Commenting Source Statements(非注釋源碼語句)
通過對非注釋源碼語句(NCSS)進(jìn)行計(jì)數(shù),確定方法耍共、類烫饼、文件的復(fù)雜度。這項(xiàng)檢查遵守Chr. Clemens Lee編寫的JavaNCSS-Tool中的規(guī)范试读。
粗略地說杠纵,NCSS度量就是不包含注釋的源代碼行數(shù),(近似)等價(jià)于分號和左花括號的計(jì)數(shù)钩骇。一個(gè)類的NCSS就是它所有方法的NCSS比藻、它的內(nèi)部類的NCSS、成員變量聲明數(shù)量的總和倘屹。一個(gè)文件的NCSS就是它所包含的所有頂層類的NCSS银亲、imports語句和包聲明語句數(shù)量的總和。
解釋:太大的方法和類會難以閱讀唐瀑,并且維護(hù)成本會很高。一個(gè)較大的NCSS數(shù)值通常意味著對應(yīng)的方法或類承擔(dān)了過多的責(zé)任和/或功能插爹,應(yīng)當(dāng)分解成多個(gè)較小的單元哄辣。
- NPath Complexity(NPath復(fù)雜度)
NPATH度量會計(jì)算遍歷一個(gè)函數(shù)時(shí)请梢,所有可能的執(zhí)行路徑的數(shù)量。它會考慮嵌套的條件語句力穗,以及由多部分組成的布爾表達(dá)式(例如毅弧,A && B,C || D当窗,等等)够坐。
解釋:在Nejmeh的團(tuán)隊(duì)中,每個(gè)單獨(dú)的例程都有一個(gè)取值為200的非正式的NPATH限值崖面;超過這個(gè)限值的函數(shù)可能會進(jìn)行進(jìn)一步的分解元咙,或者至少一探究竟。
15. Miscellaneous(雜項(xiàng):12個(gè))
- Array Type Style(數(shù)組類型風(fēng)格)
檢查數(shù)組定義的風(fēng)格巫员。有的開發(fā)者使用Java風(fēng)格:public static void main(String[] args)庶香;有的開發(fā)者使用C風(fēng)格:public static void main(String args[])。
- Descendent Token Check(后續(xù)標(biāo)記檢查)
檢查在其他標(biāo)記之下的受限標(biāo)記简识。
警告:這是一項(xiàng)非常強(qiáng)大和靈活的檢查赶掖,但是與此同時(shí),它偏向于底層技術(shù)七扰,并且非常依賴于具體實(shí)現(xiàn)奢赂,因?yàn)椋慕Y(jié)果依賴于我們用來構(gòu)建抽象語法樹的語法颈走。因此膳灶,當(dāng)其他檢查項(xiàng)目提供了你想要用的功能時(shí),我們建議你使用這些檢查項(xiàng)目疫鹊⌒湔埃總之,這項(xiàng)檢查只能在抽象語法樹的層面上工作拆吆,它并不了解任何語言結(jié)構(gòu)聋迎。
- Final Parameters(final參數(shù))
檢查方法/構(gòu)造器的參數(shù)是否是final的。這項(xiàng)檢查會忽略接口方法的檢查 —— final關(guān)鍵字不會理會接口方法的參數(shù)枣耀,因?yàn)闆]有任何代碼能夠修改這些參數(shù)霉晕。
解釋:在方法算法的執(zhí)行期間改變參數(shù)值會使讀者產(chǎn)生混淆,因此應(yīng)當(dāng)避免這種情況的發(fā)生捞奕。有個(gè)很好的方法可以使得Java的編譯器預(yù)防這種編碼風(fēng)格牺堰,那就是將方法的參數(shù)聲明為final的。
- Indentation(代碼縮進(jìn))
檢查Java代碼的縮進(jìn)是否正確颅围。
盡管有些格式精美的打印機(jī)有時(shí)可以很方便地批量重排原始代碼的格式伟葫,但是它們通常不是沒有足夠的可配置性,就是不能夠達(dá)到預(yù)期的排版格式院促。有時(shí)這是個(gè)人喜好的問題筏养,有時(shí)這是實(shí)際經(jīng)驗(yàn)的問題斧抱。無論如何,這項(xiàng)檢查應(yīng)當(dāng)只確保代碼遵守縮進(jìn)規(guī)則的一個(gè)最小集合渐溶。
- New Line At End Of File(文件末尾的新行)
檢查文件是否以新行結(jié)束辉浦。
解釋:通常,任何源碼文件和文本文件都應(yīng)當(dāng)以一個(gè)新行符結(jié)束茎辐,特別是使用諸如CVS這樣的SCM系統(tǒng)時(shí)宪郊。當(dāng)文件沒有以新行結(jié)束時(shí),CVS甚至?xí)蛴〕鲆粋€(gè)警告拖陆。
- Todo Comment(TODO注釋)
這項(xiàng)檢查負(fù)責(zé)TODO注釋的檢查弛槐。實(shí)際上,這是一種檢查Java注釋的通用正則表達(dá)式匹配器慕蔚。想要檢查其他格式的Java注釋丐黄,那么設(shè)置format屬性即可。
- Translation(語言轉(zhuǎn)換)
這是一項(xiàng)FileSetCheck檢查孔飒,通過檢查關(guān)鍵字的一致性屬性文件灌闺,它可以確保代碼的語言轉(zhuǎn)換的正確性』得椋可以使用兩個(gè)描述同一個(gè)上下文環(huán)境的屬性文件來保證一致性桂对,如果它們包含相同的關(guān)鍵字。
考慮下面的屬性文件鸠匀,它們在同一個(gè)目錄中:
#messages.properties
hello=Hello
cancel=Cancel
#messages_de.properties
hell=Hallo
ok=OK
轉(zhuǎn)換檢查將會發(fā)現(xiàn)德語hello關(guān)鍵字的拼寫錯(cuò)誤蕉斜、默認(rèn)資源文件(messages.properties)缺少ok關(guān)鍵字、德語資源文件(messages_de.properties)缺少cancel關(guān)鍵字等錯(cuò)誤:
messages_de.properties: Key 'hello' missing.
messages_de.properties: Key 'cancel' missing.
messages.properties: Key 'hell' missing.
messages.properties: Key 'ok' missing.
- Trailing Comment(行尾注釋)
這項(xiàng)檢查可以確保代碼中含有注釋的行中只包含注釋缀棍。在使用//注釋的場合下宅此,這意味著//符號之前只能有空格。如果行不是以注釋結(jié)束的爬范,那么就不會檢查這些注釋父腕。例如,下面的代碼是可接受的
Thread.sleep( 10 );
format屬性會處理“} // while”這樣的注釋青瀑。
解釋:Steve McConnell編寫的《Code Complete》認(rèn)為行尾注釋是一個(gè)不好的編程習(xí)慣璧亮。行尾注釋就是那些和實(shí)際代碼位于同一行的注釋。例如:
a = b + c; // 一條常見的注釋
d = e / f; // 這一行的另一條注釋
《Code Complete》為此給出了以下幾條論證:
注釋必須對齊斥难,這樣便不會干擾代碼的可視結(jié)構(gòu)枝嘶。如果你不將它們整潔地對齊,它們將會使你的代碼看起來就像剛從洗衣機(jī)里出來一樣亂糟糟的哑诊。
行尾注釋會很難格式化群扶,需要花費(fèi)大量的時(shí)間來對齊它們。這樣的時(shí)間并不是花在深入理解代碼上的,你只能乏味地敲擊空格鍵或制表鍵來重新格式化這些注釋竞阐。
行尾注釋非常難以維護(hù)提茁。如果某一行包含行尾注釋的代碼增加了,就會使這行的注釋被擠得更遠(yuǎn)馁菜,并且所有其他的行尾注釋為了排版對齊子漩,不得不被放置的同樣遠(yuǎn)筋岛。難以維護(hù)的代碼風(fēng)格就是不可維護(hù)的。
行尾注釋可能會意義不明確爱致。每行的右側(cè)通常不能提供足夠的空間來放置注釋毁习,將注釋和代碼放在同一行就意味著注釋可能會比較短智嚷。按照這種習(xí)慣,你編寫代碼時(shí)就會專注于每行盡可能的短纺且,而不是每行盡可能的清晰盏道。因此,這種注釋經(jīng)常會意義不明確清晰载碌。
行尾注釋還會帶來一個(gè)系統(tǒng)性問題猜嘱,你會發(fā)現(xiàn)很難僅僅在一行中寫出意義明確的注釋。大多數(shù)行尾注釋僅僅重復(fù)了一下這行的代碼嫁艇,這種行為帶來的危害性遠(yuǎn)比帶來的幫助要大朗伶。
當(dāng)使用自動(dòng)化重構(gòu)技術(shù)時(shí),源碼每行的長度會經(jīng)常變化步咪,這就使得包含大量行尾注釋的代碼變得非常難以維護(hù)论皆。
- Uncommented Main(未注釋main方法)
檢查源碼中是否有未注釋的main()方法(調(diào)試的殘留物)。
解釋:調(diào)試時(shí)經(jīng)常會在代碼中利用main()方法猾漫。當(dāng)調(diào)試結(jié)束時(shí)点晴,開發(fā)者經(jīng)常會忘記刪除這些main()方法,這樣會改變API悯周,并且會增大生成的class/jar文件的尺寸粒督。除了程序真正的入口點(diǎn)之外,源碼中其他所有的main()方法都應(yīng)當(dāng)被刪除或注釋掉队橙。
- Upper Ell(大寫“L”)
檢查long類型的常量在定義時(shí)是否由大寫的“L”開頭坠陈。注意,是“L”捐康,不是“l(fā)”仇矾。這是由《Java Language Specification》的第3.10.1章節(jié)所建議的編碼規(guī)約。
解釋:小寫字母“l(fā)”看起來非常像數(shù)字“1”解总。
- Regexp(正則表達(dá)式)
這項(xiàng)檢查可以確保指定的格式串在文件中存在贮匕,或者允許出現(xiàn)幾次,或者不存在花枫。
這項(xiàng)檢查結(jié)合了RegexpHeader刻盐、GenericIllegalRegexp和RequiredRegexp的所有功能掏膏,但是并不支持使用文件中正則表達(dá)式。
這項(xiàng)檢查的不同之處在于敦锌,它是工作在多行模式下的馒疹。它的正則表達(dá)式可以跨越多行,并且可以一次性檢查完整個(gè)文件乙墙。上面提到的其他三項(xiàng)檢查工作在單行模式下颖变。它們的單行或多行正則表達(dá)式一次只能檢查一行。它們只能依次檢查文件中的每一行听想。
注意:由于工作模式有所不同腥刹,所以需要對使用的正則表達(dá)式做一些修改才行。
在多行模式下:
“^”符號表示一行的開始汉买,而不是輸入的開始衔峰。
“\A”表示輸入的開始。
“$”符號表示一行的結(jié)束蛙粘,而不是輸入的結(jié)束垫卤。
“\Z”表示輸入的結(jié)束。
文件中的每行都是以新行符號結(jié)束出牧。
注意:并不是所有的正則表達(dá)式引擎創(chuàng)建正則表達(dá)式都是相同的葫男。有些引擎提供了額外的功能,但是其他的引擎不支持崔列,并且語法元素可能也有所不同梢褐。這項(xiàng)檢查使用了java.util.regex包,請查看相關(guān)文檔赵讯,以便于學(xué)習(xí)如何構(gòu)建一個(gè)符合使用目標(biāo)的正則表達(dá)式盈咳。
注意:當(dāng)你在XML配置文件中鍵入一個(gè)正則表達(dá)式作為參數(shù)時(shí),你必須考慮到XML文件的規(guī)則边翼,例如鱼响,如果你想要匹配一個(gè)“<”符號,那么你需要鍵入“<”组底。一條正則表達(dá)式應(yīng)當(dāng)在同一行中鍵入丈积。
- Outer Type File Name(外部類型文件名)
檢查外部類型名稱是否與文件名稱匹配。例如债鸡,類Foo必須在文件Foo.java中江滨。
16. Other(其他:2個(gè))
- Checker(檢查器)
每個(gè)checkstyle配置的根模塊。不能被刪除厌均。
- TreeWalker(樹遍歷器)
FileSetCheck TreeWalker會檢查單個(gè)的Java源碼文件唬滑,并且定義了適用于檢查這種文件的屬性。
17. Filters(過濾器:4個(gè))
- Severity Match Filter(嚴(yán)重度匹配過濾器)
SeverityMatchFilter過濾器會根據(jù)事件的嚴(yán)重級別決定是否要接受審計(jì)事件。
- Suppression Filter(抑制過濾器)
在檢查錯(cuò)誤時(shí)晶密,SuppressionFilter過濾器會依照一個(gè)XML格式的策略抑制文件擒悬,選擇性地拒絕一些審計(jì)事件。如果沒有配置好的策略抑制文件可用稻艰,那么這個(gè)過濾器會接受所有的審計(jì)事件懂牧。
- Suppression Comment Filter(抑制注釋過濾器)
SuppressionCommentFilter過濾器使用配對的注釋來抑制審計(jì)事件。
解釋:有時(shí)候尊勿,違反一項(xiàng)檢查是有正當(dāng)理由的归苍。當(dāng)問題出在代碼本身,而不是個(gè)人喜好時(shí)运怖,最好在代碼中覆蓋檢查策略。半結(jié)構(gòu)化的注釋可以和檢查關(guān)聯(lián)起來夏伊。這種做法有時(shí)比一個(gè)獨(dú)立的策略抑制文件更好摇展,因?yàn)檫@個(gè)文件必須隨著源碼文件的變化而保持更新。
- Suppress With Nearby Comment Filter(抑制附近注釋過濾器)
SuppressWithNearbyCommentFilter過濾器使用獨(dú)立的注釋來抑制審計(jì)事件溺忧。
解釋:和SuppressionCommentFilter相同咏连。然而,SuppressionCommentFilter使用配對的過濾器來打開/關(guān)閉注釋匹配鲁森,SuppressWithNearbyCommentFilter則使用單個(gè)注釋過濾器祟滴。這樣可以使用更少的行數(shù)來標(biāo)記一塊區(qū)域,在某些環(huán)境中會顯得風(fēng)格很優(yōu)美歌溉。
用法:這個(gè)過濾器需要和FileContentsHolder協(xié)同使用垄懂,因?yàn)闄z查器可以在.java文件中不公開地使用抑制注釋。包含這個(gè)過濾器的配置文件必須將FileContentsHolder配置為TreeWalker的一個(gè)子模塊痛垛。