Checkstyle 是 SourceForge 下的一个项目,如名所示,是检查 java 程序源代码编码风格的,是 java 程序员不可或缺的工具,用以检查自己的编码习惯是否符合公司的要求或 Sun 的约定。 Checkstyle 是目前最广泛使用的代码检查工具 , 功能强大,操作简单,可以和 Ant 结合使用,最重要的是其是 Open Source 的。提供了一个帮助 JAVA 开发人员遵守某些编码规范的工具。
CheckStyle 默认提供一些主要检查内容:
· Javadoc 注释
·命名约定
·标题
· Import 语句
·体积大小
·空白
·修饰符
·块
·代码问题
·类设计
·混合检查(包括一些有用的,非必须的 System.out 和 printstackTrace )
从上面可以看出, CheckStyle 提供的大部分功能都是对于代码规范的检查,而没有提供象 PMD 和 Jalopy 那样的增强代码质量和修改代码的功能。但是,对于团队开发,尤其是强调代码规范的公司来说,它的功能已经足够强大。 CheckStyle 默认提供了对 Sun 编程规范的支持。通过编写配置文件,可以增加自己的检查规范。
CheckStyle 的配置文件是基于 XML 而非 Properties 文件。它提供了两种运行的方式: 命令行工具和 ANT 任务。另外 CheckStyle 目前有很多针对流行 IDE 的插件,例如 Eclipse 、 IntelliJ IDEA 、 JBuilder 等。
1、 和 Ant 配合使用
从 http://sourceforge.net/project/showfiles.php?group_id=29721 下载最新版本的 CheckStyle ,目前的版本是 4.3 。把解压后目录复制到项目的 lib 目录下,然后就可以和 Ant 配合使用了。
在 Ant 脚本中,首先定义 CheckStyle 的解压目录位置:
<path id= "checkstyle.path" >
<fileset dir= "${lib.home}/checkstyle-4.3" >
<include name= "**/*.jar" />
</fileset>
</path>
接着声明 CheckStyle 任务:
<taskdef resource= "checkstyletask.properties" classpathref= "checkstyle.path" />
然后建立 CheckStyle 任务:
<target name= "checkstyle" >
<!-- 指明 checkstyle 的分析所需的规则文件 -->
<checkstyle config= "${lib.home}/checkstyle-4.3/sun_checks.xml"
failureProperty= "checkstyle.failure"
failOnViolation= "false" >
<!-- 指明输出格式和文件名 -->
<formatter type = "xml" tofile = "d:/report.xml" />
<!— 指明要检查的文件 -->
<fileset dir = "${src.home}" >
<include name = "**/*.java" />
</fileset>
</checkstyle>
<!-- 将 xml 文件转换成 html 文件 -->
<style in= "d:/report.xml" out = "d:/report.html"
style= "${lib.home}/checkstyle-4.3/contrib/checkstyle-noframes.xsl" />
</target>
最后运行 ant checkstyle 即可。
注: checkstyle 提供了缺省的 xslt ,用来进行 xml 的格式转换。它们都放在 contrib 目录中。 checkstyle 同样也提供了自定义的 check ,但与 PMD 相比,书写要复杂。详细情况请参见 checkstyle 的文档。
2、 Eclipse 插件
在 Eclipse 当中安装 CheckStyle 插件非常方便,可以参照以下五个步骤进行:
a. 在 Eclipse 当中分别打开一下菜单 Help->Software Updates->Find and Install
b. 选择 Search for new features to install 选项然后点击 Next
c. 新建一个 New Remote Site...
d. 输入名称(比如: CheckStyle Plug-in )和下面的 URL : http://eclipse-cs.sourceforge.net/update
e. 开始安装
安装好 CheckStyle 以后,不符合 CheckStyle 编码规范的信息将会出现在“问题 (Problems) ”视图当中,可以点击问题视图的“过滤器( Filters )”配置显示内容;
选择“ Window - > Preferences -> Checkstyle ”进入 CheckStyle 的配置页面。 CheckStyle 默认的 sun 规范检查文件是: sun_checks.xml , CheckStyle 的配置文件的导入 / 导出就在配置页面右侧。我们也可以导出以后,经过修改量身定做自己的规范。比如我们已经按照规范做好了一个代码规范文件,并且取名为“我的软件”,那么我们可以把这个规范导入,在 location 中加入自定义的 xml 文件, Type 选择 External Configuration File ,名称填上:“我的软件”,然后填上相应的描述,然后点击 browse 按钮找到相应的配置文档,点击 ok 就可以了。这样 checkstyle 当中又多了一个叫做“我的软件”的规范。
CheckStyle 是一个非常复杂的代码风格检查过程:包括缩进,命名规范等,因此缺省是 Disable 的,启动针对一个项目的 CheckStyle 需要用右键点击项目,在弹出的菜单中选择“ Checkstyle - >Check Code with Checkstyle ”。 这样,我们就能使用 CheckStyle 对文档进行规范了。下面这个例子是使用 CheckStyle 以后,在 eclipse 中对不符合规范的代码进行标注,这样我们就可以按照“我的软件 ” 规范对代码进行约束了。下图当中左边的放大镜图标表示此行代码不符合规范,当鼠标悬停在上面的时候,会显示这行代码的问题所在。(图略)
3、 定制 CheckStyle
在 CheckStyle 的最新发布版本中,有一个对于 Sun 的 Java 编码规范的配置文件信息。但是,其中有很多条目并不一定符合项目开发的需要。就算是对于很多优秀的开源项目,按照这个规范来进行检查,也会出现成千上万的错误。这就是为什么需要定制 CheckStyle 的原因。
CheckStyle 的执行基于 XML 配置文件,它的主要组成部分是:
· Module :整个配置文件就是一棵 Module 树。根节点是 Checker Module 。
· Properties :它来决定一个 Module 如何进行检查。每个 Module 都有一个默认值,如果不满足开发需求,可以设定其它的值。
下面是一个示例:
< module name="MethodLength" >
< property name="max" value="60"/ >
< /module >
它表示,如果方法或者构造函数的长度超过 60 行, CheckStyle 就会报错。而默认值是 150 行。一般情况下,与 IDE 集成在一起使用的时候,点击出错的条目,可以跳转到相应的代码。
对于所有的模块来书,如果有这个模块则说明检测这一项,没有则不检测这一项。所有的模块中,其 ROOT 必须为 Checker 。配置文件中某些元素的具体讲解如下 。
<? xml version ="1.0" encoding ="UTF-8" ?>
<! DOCTYPE module PUBLIC "-//Puppy Crawl//DTD CheckeConfiguration 1.2//EN"
"http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
<! -- 对于所有的模块来说, 如果有这个模块则说明检测这一项, 没有则不检测这一项 -- >
<! -- 所有的模块中, 其ROOT 必须为Checker -- >
< module name ="Checker">
<! -- 检验每个包是否存在package.html 文件-- >
<! -- See http://checkstyle.sf.net/config_javadoc.html#PackageHtml -- >
<! --
<module name="PackageHtml"/>
-- >
<! -- 检验每个文件末尾是否有一个空行, 在UNIT 机器上是有的, 在CVS 上如果没有会有警告的-- >
<! -- See http://checkstyle.sf.net/config_misc.html#NewlineAtEndOfFile -- >
<! --
<module name="NewlineAtEndOfFile"/>
-- >
<! -- Checks that property files contain the same keys. -- >
<! -- See http://checkstyle.sf.net/config_misc.html#Translation -- >
< module name ="Translation" />
< module name ="TreeWalker">
<! -- Checks for Javadoc comments. -- >
<! -- See http://checkstyle.sf.net/config_javadoc.html -- >
<! -- Checks Javadoc commentsefor method definitions.-- >
< module name ="JavadocMethod">
< property name ="scope" value ="public" />
<! -- 是否允许错误的参数声明,true 为允许, 缺省为不允许 -- >
< property name ="allowMissingParamTags" value ="true" />
<! -- 是否允许错误的错误声明,true 为允许, 缺省为不允许 -- >
< property name ="allowMissingThrowsTags" value ="true" />
<! -- 是否允许错误的返回类型声明,true 为允许, 缺省为不允许 -- >
< property name ="allowMissingReturnTag" value ="true" />
</ module >
<! --Checks Javadoc comments for class and interface definitions.-- >
< module name ="JavadocType" />
<! -- Checks that variables have Javadoc comments.-- >
< module name ="JavadocVariable">
< property name ="scope" value ="protected" />
</ module >
<! -- 检查Javadoc 的格式 -- >
< module name ="JavadocStyle">
< property name ="scope" value ="public" />
<! -- Comment 的第一句的末尾是否要有一个句号,true 必须有,default 为true -- >
< property name ="checkFirstSentence" value ="false" />
<! -- 检查错误的HTML 脚本, 比如不匹配,true 检查,default 为true -- >
< property name ="checkHtml" value ="true" />
</ module >
<! -- Checks for Naming Conventions. -- >
<! -- See http://checkstyle.sf.net/config_naming.html -- >
<! -- 确省必须以Abstract 开始或者以Factory 结束 -- >
<! --
<module name="AbstractClassName"/>
-- >
< module name ="ConstantName" />
< module name ="LocalFinalVariableName" />
< module name ="LocalVariableName" />
< module name ="MemberName" />
< module name ="MethodName" />
< module name ="PackageName" />
< module name ="ParameterName" />
< module name ="StaticVariableName" />
< module name ="TypeName" />
<! -- Checks for Headers -- >
<! -- See http://checkstyle.sf.net/config_header.html -- >
<! -- 检查文件是否以指定文件开始, 这里最好是放一些版权信息和工程描述 -- >
<! -- headerFile: 指定的文件 -- >
<! -- ignoreLines: 忽略哪些行, 以"," 分隔 -- >
<! --
<module name="Header">
<property name="headerFile" value="java.header"/>
<property name="ignoreLines" value="2, 3, 4, 5"/>
</module>
-- >
<! -- Following interprets the header file as regular expressions. -- >
<! --
<module name="RegexpHeader"/>
-- >
<! -- Checks for imports -- >
<! -- See http://checkstyle.sf.net/config_import.html -- >
<! -- 检查使用* 号的导入, 默认为全部类 -- >
< module name ="AvoidStarImport" />
<! -- 检查是否有非法的包, 确省检查sun.*; 对于某些包是不建议直接调用的 -- >
< module name ="IllegalImport">
< property name ="illegalPkgs" value ="sun.*" />
</ module >
<! -- 检查多于的导入, 如一个类导入了多次 -- >
< module name ="RedundantImport" />
<! -- 检查没有使用的导入 -- >
< module name ="UnusedImports" />
<! -- 导入排序 -- >
<! -- groups: 分组, 哪些是一组的 -- >
<! -- ordered: 同一个组内是否排序,true 排序, 确省为true -- >
<! -- separated: 各个组之间是否需要用空行分隔, 确省为false -- >
<! -- caseSensitive: 是否是大小写敏感的, 确省是 -- >
<! --
<module name="ImportOrder">
<property name="groups" value="java,javax"/>
<property name="ordered" value="true"/>
<property name="separated"evalue="true"/>
<property name="caseSensitive" value="true"/>
</module>
-- >
<! -- Checks for Size Violations. -- >
<! -- See http://checkstyle.sf.net/config_sizes.html -- >
<! -- 检查方法内可执行语句的个数, 确省为30 行 -- >
<! --
<module name="ExecutableStatementCount">
<property name="max" value="30"/>
</module>
-- >
<! -- 文件的最大行数, 缺省为1500 -- >
< module name ="FileLength">
< property name ="max" value ="2000" />
</ module >
<! -- 每行的最大字符数, 缺省为80 -- >
< module name ="LineLength">
<! -- 忽略指定格式的行, 如* 号开始的, 等 -- >
<! --
<property name="ignorePattern" value="^ ** *[^ ]+$"/>
-- >
< property name ="max" value ="120" />
</ module >
<! -- 方法的最大行数, 缺省为150 -- >
< module name ="MethodLength">
< property name ="max" value ="200" />
<! -- 统计时是否包括空行和以// 开始的注释, 缺省为统计(true)-- >
< property name ="countEmpty" value ="false" />
</ module >
<! -- 匿名类的最大行数, 缺省为20 -- >
< module name ="AnonInnerLength">
< property name ="max" value ="60" />
</ module >
<! -- 检查方法和构造子参数的最大个数, 缺省为7 -- >
< module name ="ParameterNumber" />
<! -- Checks for whitespace -- >
<! -- See http://checkstyle.sf.net/config_whitespace.html -- >
< module name ="EmptyForInitializerPad" />
< module name ="EmptyForIteratorPad" />
< module name ="MethodParamPad">
< property name ="allowLineBreaks" value ="true" />
</ module >
< module name ="NoWhitespaceAfter" />
< module name ="NoWhitespaceBefore" />
< module name ="OperatorWrap" />
< module name ="ParenPad" />
< module name ="TypecastParenPad" />
< module name ="TabCharacter" />
< module name ="WhitespaceAfter" />
< module name ="WhitespaceAround" />
<! -- Modifier Checks -- >
<! -- See http://checkstyle.sf.net/config_modifiers.html -- >
<! -- 要求JLS suggestions -- >
<! --
<module name="ModifierOrder"/>
-- >
< module name ="RedundantModifier" />
<! -- Checks for blocks. You know, those {}'s -- >
<! -- See http://checkstyle.sf.net/config_blocks.html -- >
<! -- 检查空块 -- >
<! --
<module name="EmptyBlock"/>
-- >
< module name ="LeftCurly" />
<! -- 检查只有必须有{}, 确省为必须, 主要在if,else 时有这样的情况 -- >
< module name ="NeedBraces" />
<! -- 检查"}", 确省在同一行 -- >
< module name ="RightCurly">
< property name ="option" value ="alone" />
</ module >
<! -- 检查多余嵌套的{}, 请看文档, 不易说明 -- >
< module name ="AvoidNestedBlocks" />
<! -- Checks for common coding problems -- >
<! -- See http://checkstyle.sf.net/config_coding.html -- >
< module name ="AvoidInlineConditionals" />
< module name ="CovariantEquals" />
< module name ="DeclarationOrder" />
< module name ="DefaultComesLast" />
< module name ="DoubleCheckedLocking" />
<! --
<module name="EmptyStatement"/>
-- >
< module name ="EqualsHashCode" />
<! -- 变量必须初始化为自己的类型, 如果给一个Object 类型的变量初始化为null 会提示 -- >
<! --
<module name="ExplicitInitialization"/>
-- >
< module name ="FallThrough" />
<! --
<module name="FinalLocalVariable"/>
-- >
< module name ="HiddenField">
< property name ="ignoreConstructorParameter" value ="true" />
< property name ="ignoreSetter" value ="true" />
</ module >
<! -- Exception, Throwable, RuntimeException 是不允许catch 的 -- >
<! --
<module name="IllegalCatch"/>
-- >
< module name ="IllegalInstantiation" />
<! -- 有一个bug, 比如i++ 居然都不认
<module name="IllegalToken"/>
-- >
< module name ="IllegalTokenText" />
< module name ="IllegalType" />
< module name ="InnerAssignment" />
<! -- 检查直接数
<module name="MagicNumber"/>
检查是否有构造子
<module name="MissingCtor"/>
-- >
< module name ="MissingSwitchDefault" />
< module name ="MultipleVariableDeclarations" />
<! --
<module name="JUnitTestCase"/>
<module name="NestedIfDepth"">
<property name="max" value="5"/>
</module>
<module name="NestedTryDepth"">
<property name="max" value="5"/>
</module>
<module name="PackageDeclaration"/>
<module name="ReturnCount"/>
-- >
<! -- 不能为参数付值 -- >
<! --
<module name="ParameterAssignment"/>
-- >
< module name ="RedundantThrows" />
<! -- 不能理解的, 好像是bug
<module name="RequireThis"/>
-- >
< module name ="SimplifyBooleanExpression" />
< module name ="SimplifyBooleanReturn" />
< module name ="StringLiteralEquality" />
< module name ="SuperClone" />
< module name ="SuperFinalize" />
< module name ="UnnecessaryParentheses" />
<! -- Checks for class design -- >
<! -- See http://checkstyle.sf.net/config_design.html -- >
<! -- 要求一个方法必须声明为Extension 的, 否则必声明为abstract, final or empty -- >
<! --
<module name="DesignForExtension"/>
-- >
<! -- 检查private 构造子是否声明为final, 这里有个问题, 在Java 中构造子是不能声明为final 的 -- >
<! --
<module name="FinalClass"/>
-- >
<! -- 要求一定要有一个构造子 -- >
<! --
<module name="HideUtilityClassConstructor"/>
-- >
< module name ="InterfaceIsType" />
<! -- 检查变量的可见性, 确省只允许static final 为public, 否则只能为private -- >
< module name ="VisibilityModifier">
< property name ="packageAllowed" value ="true" />
< property name ="protectedAllowed" value ="true" />
</ module >
<! --
<module name="MutableException"/>
-- >
<! -- 限制抛出声明的指定数量, 确省为1 -- >
<! --
<module name="ThrowsCount"/>
-- >
<! -- Miscellaneous other checks. -- >
<! -- See http://checkstyle.sf.net/config_misc.html -- >
<! -- 数组的声明是否允许Java 的类型, 确省为允许,Java 类型为String[] xx,C++ 的类型为String xx[]; -- >
< module name ="ArrayTypeStyle" />
<! --
<module name="FinalParameters"/>
-- >
<! -- 一般性的代码问题, 不好的习惯等, 可以多 -- >
<! -- 文件中使用了System.out.print 等-- >
< module name ="GenericIllegalRegexp">
< property name ="format" value ="System.out.print" />
< property name ="message" value ="bad practice of use System.out.print" />
</ module >
< module name ="GenericIllegalRegexp">
< property name ="format" value ="System.exit" />
< property name ="message" value ="bad practice of use System.exit" />
</ module >
< module name ="GenericIllegalRegexp">
< property name ="format" value ="printStackTrace" />
< property name ="message" value ="bad practice of use printStackTrace" />
</ module >
<! -- 关于Task, 你可以声明自己的Task 标识 -- >
< module name ="TodoComment">
< property name ="format" value ="TODO" />
</ module >
<! -- 强迫// 注释必须如何, 入下要求只能有一行, 具体看文档 -- >
<! --
<module name="TrailingComment">
<property name="format" value="^s*$"/>
</module>
-- >
<! -- main 方法经常会在debug 时使用, 但发行版本的时候可能并不需要这个方法, 提示 -- >
<! --
<module name="UncommentedMain"/>
-- >
<! -- 当定义一个常量时, 希望使用大写的L 来代替小写的l, 原因是小写的l 和数字1 很象 -- >
< module name ="UpperEll" />
<! -- 检查正确的缩进, 这个更象是个人习惯 -- >
<! --
<module name="Indentation">
<property name="braceAdjustment" value="0"/>
</module>
-- >
<! -- Checks For Metrics -- >
<! -- See http://checkstyle.sf.net/config_metrics.html -- >
<! -- 检查嵌套复杂度 -- >
< module name ="CyclomaticComplexity">
< property name ="max" value ="12" />
</ module >
</ module >
</ module >