本文主要介绍CheckStyle 的各个规则配置项目,这个版本的CheckStyle将样式规则分为了如下十六种类型共138条规则:html
官方文档地址:http://checkstyle.sourceforge.net/index.htmljava
这项检查能够控制要使用的注解的位置。程序员
这项检查能够控制要使用的注解的样式。正则表达式
检查java.lang.Deprecated注解或@deprecated的Javadoc标记是否同时存在。算法
当出现{@inheritDoc}的Javadoc标签时,验证java.lang.Override注解是否出现。编程
这项检查能够确保全部包的注解都在package-info.java文件中。数组
这项检查容许你指定不容许SuppressWarnings抑制哪些警告信息。你还能够指定一个TokenTypes列表,其中包含了全部不能被抑制的警告信息。缓存
//这项检查。安全
找到嵌套代码块,也就是在代码中无节制使用的代码块。数据结构
解释:内嵌代码块一般是调试过程的残留物,它们会使读者产生混淆。
检查空代码块。
//TODO...
检查代码块的左花括号的放置位置。
经过property选项指定验证策略。
若使用eol和nlow策略,则须要考虑maxLineLength属性。
检查代码块周围是否有大括号,能够检查do、else、if、for、while等关键字所控制的代码块。
检查else、try、catch标记的代码块的右花括号的放置位置。
经过property选项指定验证策略。
检查类是否具备可扩展性。更准确地说,它强制使用一种编程风格,父类必须提供空的“句柄”,以便于子类实现它们。
确切的规则是,类中能够由子类继承的非私有、非静态方法必须是:
1. abstract方法,或
2. final方法,或
3. 有一个空的实现
解释:这种API设计风格能够保护父类不会被子类破坏。不利之处在于子类的灵活性会受到限制,特别是它们不可以阻止父类代码的执行,可是这也意味着子类不会因为忘记调用父类的方法而破坏父类的状态。(我的理解:不容许类的方法被子类覆盖)
检查一个只有私有构造器的类是否被声明为final。
确保工具类(在API中只有静态方法和字段的类)没有任何公有构造器。
解释:实例化工具类没有任何意义。所以,工具类的构造器应当是私有的或者受保护的(若是你打算之后扩展子类)。一个常见的错误即是忘记隐藏默认构造器。
若是你打算将工具类的构造器声明为受保护的,那么你能够考虑下面的构造器实现技术,借此能够禁止子类的实例化:
public class StringUtils // 不是final类,容许子类继承
{
protected StringUtils() {
throw new UnsupportedOperationException(); // 防止子类调用
}
public static int count(char c, String s) {
// ...
}
}
检查嵌套/内部的类型是否在当前类的最底部声明(在全部的方法/字段的声明以后)。
Bloch编写的《Effective Java》中提到,接口应当描述为一个类型。所以,定义一个只包含常量,可是没有包含任何方法的接口是不合适的。标准类javax.swing.SwingConstants是一个会被这项检查标记的示例类。
这项检查还能够配置为禁用标记接口,例如java.io.Serializable,这种接口不会包含任何方法或常量。
确保异常(异常类的名称必须匹配指定的正则表达式)是不可变的。也就是说,异常只能有final字段。
这项检查当前使用的算法很是简单,它会检查异常的全部成员是不是final的。用户仍然能够修改一个异常实例(例如,Throwable使用setStackTrace(StackTraceElement[] stackTrace)方法修改堆栈跟踪)。可是,至少这种异常类型所提供的信息是不可修改的。
解释:异常实例应当表示一个错误状态。异常类中含有非final的字段,不只仅会致使异常状态会因为偶然的因素被修改,这样便会遮蔽原始的异常状态,还会使得开发者偶尔会忘记初始化异常状态,这样便会致使代码捕捉到异常以后,根据异常状态推导出不正确的结论。
//TODO...
这项检查还能够配置为禁用标记接口,例如java.io.Serializable,这种接口不会包含任何方法或常量。
将异常抛出语句的数量配置为一个指定的限值(默认值为1)。
解释:异常是方法接口的组成部分之一。若是一个方法声明抛出过多不一样的异常,就会使得异常处理很是繁重,而且会致使很差的编程习惯,例如catch (Exception)。这项检查会强制开发者将异常处理变得具备层次性,举个最简单的例子,调用者只须要检查一种类型的异常,可是必要时也容许捕捉上述异常的任何子类。
检查类成员的可见性。只有static final的类成员能够是公有的,其余的类成员必须是私有的,除非设置了protectedAllowed属性或packageAllowed属性。
若是类成员的名称和指定的公有成员正则表达式匹配,那么这项检查就不会标记这个类成员(默认包含“^serialVersionUID$”)。
解释:强制封装。
//TODO...
检测内联条件语句。内联条件语句的一个示例以下所示:
String a = getParameter("a");
String b = (a==null || a.length<1) ? null : a.substring(1);
解释:有些开发者发现内联条件语句很难读懂,所以他们公司的编码标准会禁止使用内联条件语句。
检查定义了共变equals()方法的类中是否一样覆盖了equals(java.lang.Object)方法。这项检查受到FindBugs的启发。
解释:错误地定义了一个共变equals()方法,而没有覆盖equals(java.lang.Object)方法,可能会产生不可预料的运行时行为。
根据Java编程语言的编码规约,一个类或接口的声明部分应当按照如下顺序出现:
1. 类(静态)变量。首先应当是public类变量,而后是protected类变量,而后是package类变量(没有访问标识符),最后是private类变量。
2. 实例变量。首先应当是public类变量,而后是protected类变量,而后是package类变量(没有访问标识符),最后是private类变量。
3. 构造器
4. 方法
检查switch语句中的default是否在全部的case分支以后。
解释:Java容许default位于switch语句中的任何地方。可是,若是default位于最后一个case分支以后,那么代码的可读性会更强。
检测代码中是否有空语句(也就是单独的;符号)。
检查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()方法的类是否也覆盖了hashCode()方法。
解释:equals()方法和hashCode()方法约定,相等的对象必然具备相同的哈希码。所以,只要你覆盖了equals()方法,你就必须同时覆盖hashCode()方法,以确保能够在基于哈希的集合中使用你的类。
检查类或对象的成员是否显式地初始化为成员所属类型的默认值(对象引用的默认值为null,数值和字符类型的默认值为0,布尔类型的默认值为false)。
解释:每一个实例变量都会被初始化两次,而且初始化为相同的值。在执行代码中指定的任何初始化操做以前,Java会初始化每一个实例变量为它的默认值(0或null)。所以在这种状况下,x会被初始化为0两次,bar会被初始化为null两次。所以,这样稍微有些效率低下。这种编码风格是C/C++编码风格的延续,它代表开发者并非真正有把握Java可以初始化实例变量为它的默认值。
检查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。这项检查还能够被配置为检查未修改过的参数是否被声明为final。
当配置为检查参数时,这项检查会忽略接口方法和抽象方法中的参数。
检查局部变量或参数是否会遮蔽在相同类中定义的字段。
从不容许捕捉java.lang.Exception、java.lang.Error、java.lang.RuntimeException的行为。
解释:缺少经验的开发者常常会简单地捕捉Exception异常,试图处理多种异常类型。这会很不幸地使代码无心中捕捉到NullPointerException、OutOfMemoryErrors等系统异常。
检查是否有不合法的实例化操做,是否使用工厂方法更好。
解释:根据不一样的项目,对于某些类来讲,可能经过工厂方法来建立类实例更好,而不是调用类构造器。
一个简单的示例就是java.lang.Boolean类。为了节省内存和CPU周期,最好使用预约义的常量TRUE和FALSE。构造器的调用应当被替换为调用Boolean.valueOf()方法。
某些对性能有极端要求的项目可能须要其余的类也使用工厂方法,以便于提升缓存或对象池的使用效率。
这项检查能够用来确保类型不能声明抛出指定的异常类型。从不容许声明抛出java.lang.Error或java.lang.RuntimeException。
检查不合法的标记。
解释:某个语言特性常常会致使代码难以维护,或者开发新手难以理解。在某些框架中,其余特性可能不推荐使用,例如,在EJB组件中最好不要使用本地方法。
检查是否有不合法的标记文本。
检查代码中是否有在变量声明、返回值、参数中都没有做为类型使用过的特定类。包括一种格式检查功能,默认状况下不容许抽象类。
解释:帮助减小和实体类之间的耦合。另外,抽象类应当被认为是接口的一种简便的基类实现,所以不能是类型自己。
检查子表达式中是否有赋值语句,例如String s = Integer.toString(i = 2);。
解释:这项检查会忽略for循环代码,其他全部的赋值操做都应当在它们本身的顶层语句中,以便于加强可读性。在上述的内部赋值代码中,很难看到变量是在哪儿赋值的。
检查代码中是否含有“幻数”,幻数就是没有被定义为常量的数值文字。默认状况下,-一、0、一、2不会被认为是幻数。
检查类(除了抽象类)是否认义了一个构造器,而不是依赖于默认构造器。
检查switch语句是否含有default子句。
解释:在每一个switch语句中引入一条默认分支一般是一个很好的主意。即便开发者确信全部当前可能的分支都能覆盖到,这也应当在default分支中表达出来,例如,使用一条断言。这种方法使得代码能够应付之后的修改,例如,在一个枚举类型中引入新的类型。
检查确保for循环的控制变量没有在for代码块中被修改。示例代码以下:
for (int i = 0; i < 1; i++) {
i++;
}
解释:若是在循环体中修改了控制变量,程序流程就会变得更加难以跟踪。能够用while循环替换for循环。
检查在单个文件中,相同的字符串常量是否出现了屡次。
解释:重复代码会使得维护工做变得更加困难,所以最好用一个常量来替换屡次出现。
检查每一个变量是否使用一行一条语句进行声明。
解释:《SUN编码规约》的第6.1章节推荐应当使用一行一条语句声明一个变量。
限制for循环的嵌套层数(默认值为1)。
限制if-else代码块的嵌套层数(默认值为1)。
限制try代码块的嵌套层数(默认值为1)。
检查是否覆盖了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}几乎彻底相同。
验证类中是否认义了finalize()方法。
每行一条语句。
检查重载方法被放在一块儿。
确保一个类具备一个包声明,而且(可选地)包名要与源代码文件所在的目录名相匹配。
解释:位于空包中的类是不可以被导入的。不少开发新手并无注意到这一点。
不容许对参数进行赋值。
解释:对参数的赋值一般被认为是缺少编程实践经验。强迫开发者将参数声明为final一般是很是麻烦的。这项检查能够确保参数从不会被赋值,这对于双方都是好事。
检查代码是否使用了“this.”,也就是说,在默认状况下,引用当前对象的实例变量和方法时,应当显式地经过“this.varName”或“this.methodName(args)”这种形式进行调用。
限制return语句的数量。默认值为2。能够忽略检查指定的方法(默认忽略equals()方法)。
解释:过多的返回点可能代表代码尝试处理过多的业务,可能会难以理解。
检查是否有过于复杂的布尔表达式。如今可以发现诸如if (b == true)、b || true、!false等类型的代码。
解释:复杂的布尔逻辑会使得代码难以理解和维护。
检查是否有过于复杂的布尔类型return语句。例以下面的代码:
if (valid())
return false;
else
return true;
能够写成:
return !valid();
这项检查是从PMD规则中借鉴而来的。
检查字符串对象的比较是否使用了==或!=运算符。
解释:Java新手程序员常常会使用相似于下面的代码:
if (x == "something")
其实他们是想表达以下的意思:
if ("something".equals(x))
检查一个覆盖的clone()方法是否调用了super.clone()方法。
参考:Object.clone()。
检查一个覆盖的finalize()方法是否调用了super.finalize()方法。
参考:清理未使用对象。
检查代码中是否使用了没必要要的圆括号。
//TODO...
检查源码文件是否开始于一个指定的文件头。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行。
检查Java源码文件头部的每行是否匹配指定的正则表达式。
解释:在某些项目中,检查固定的头部是不足够的,例如,文件头可能须要一行版权信息,可是其中的年份信息会随着时间变化。
例如,考虑下面的文件头:
line 1: ^/{71}$
line 2: ^// checkstyle:$
line 3: ^// Checks Java source code for adherence to a set of rules\.$
line 4: ^// Copyright [Math Processing Error] \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注释中的冲突)。
检查没有import语句使用*符号。
解释:从一个包中导入全部的类会致使包之间的紧耦合,当一个新版本的库引入了命名冲突时,这样就有可能致使问题发生。
检查没有静态导入语句。
解释:导入静态成员可能会致使类成员之间的命名冲突。导入静态成员可能会致使代码的可读性不好,由于读者可能会搞不清楚成员到底位于哪一个类中。
//TODO...
检查是否导入了指定的非法包。默认状况下,这项检查会拒绝全部的sun.*包,由于直接使用sun.*包的程序确定不是100%的纯Java程序。想要拒绝其余的包,将illegalPkgs属性设置为你指定的非法包列表便可。
控制容许导入每一个包中的哪些类。可用于确保应用程序的分层规则不会违法,特别是在大型项目中。
导入控制XML文档所使用的DTD文件位于
http://www.puppycrawl.com/dtds/import_control_1_0.dtd。它包含了上述XML文档的全部元素和属性。
检查导入包的顺序/分组。确保导入包的分组按照指定的顺序排列(例如,java.排在首位,javax.排在第二,以此类推),而且每一个分组内导入的包都是按照字典序排列的。静态导入必须放在最后,而且也是按照字典序排列的。
检查是否存在多余的导入语句。若是一条导入语句知足如下条件,那么就是多余的:
1. 它是另外一条导入语句的重复。也就是,一个类被导入了屡次。
2. 从java.lang包中导入类,例如,导入java.lang.String。
3. 从当前包中导入类。
检查未使用的导入语句。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}
检查文档标签顺序,默认:@author, @version, @param, @return, @throws, @exception, @see, @since, @serial, @serialField, @serialData, @deprecated
检查方法或构造器的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)
检查每一个Java包是否都有Javadoc注释。默认状况下,它只容许使用一个package-info.java文件,可是能够将其配置为容许使用package.html文件。
若是两个文件都存在的话,就会报告错误,由于Javadoc工具不容许这种状况发生。
检查文档注释段落
验证Javadoc注释,以便于确保它们的格式。能够检查如下注释:接口声明、类声明、方法声明、构造器声明、变量声明。
TODO...
检查方法或构造器的Javadoc。默认不会检查author和version标记。想要验证的可见范围是由scope属性指定的,默认值为Scope.PRIVATE。想要验证其余的可见范围,则须要将scope属性设置为相应的值。想要为author标记或version标记定义格式,将authorFormat属性或versionFormat属性设置为指定的正则表达式便可。
若是不想显示因为类型参数没有在注释中使用param标记进行说明而产生的错误消息,就须要勾选allowMissingParamTags属性。
检查变量是否具备Javadoc注释。
TODO...
TODO...
TODO...
将Javadoc做为信息输出。能够做为样式表来使用,根据做者名排序报告。想要为一个标记定义格式,将tagFormat属性设置为相应的正则表达式便可。这项检查会使用两种不一样的严重级别。当标记缺失时,使用标准的严重级别(Severity)。当标记存在时,使用附加的严重级别(tagSeverity)做为报告等级。
限制一个表达式中的&&、||、&、|、^等逻辑运算符的数量。
解释:过多的条件会致使代码难以读懂、调试和维护。
注意,&和|运算符并不只仅是整数的位运算符,它们仍是布尔运算符&&和||的非快捷版本。
这项度量会测量给定类中的其余类的实例化操做的次数。这种类型的耦合并非因为继承或者面向对象范型而产生的。通常而言,任何将其余抽象数据类型做为成员的抽象数据类型都具备数据抽象耦合;所以,若是一个类中的某个局部变量是另外一个类的实例(对象),那么就存在数据抽象耦合(DAC)。DAC越高,系统的数据结构(类)就会越复杂。
一个给定类所依赖的其余类的数量。这个数量的平方还能够用于表示函数式程序(基于文件)中须要维护总量的最小值。
检查循环复杂度是否超出了指定的限值。该复杂度由构造器、方法、静态初始化程序、实例初始化程序中的if、while、do、for、?:、catch、switch、case等语句,以及&&和||运算符的数量所测量。它是遍历代码的可能路径的一个最小数量测量,所以也是须要的测试用例的数量。一般1-4是很好的结果,5-7较好,8-10就须要考虑重构代码了,若是大于11,则须要立刻重构代码!
经过对非注释源码语句(NCSS)进行计数,肯定方法、类、文件的复杂度。这项检查遵照Chr. Clemens Lee编写的JavaNCSS-Tool中的规范。
粗略地说,NCSS度量就是不包含注释的源代码行数,(近似)等价于分号和左花括号的计数。一个类的NCSS就是它全部方法的NCSS、它的内部类的NCSS、成员变量声明数量的总和。一个文件的NCSS就是它所包含的全部顶层类的NCSS、imports语句和包声明语句数量的总和。
解释:太大的方法和类会难以阅读,而且维护成本会很高。一个较大的NCSS数值一般意味着对应的方法或类承担了过多的责任和/或功能,应当分解成多个较小的单元。
NPATH度量会计算遍历一个函数时,全部可能的执行路径的数量。它会考虑嵌套的条件语句,以及由多部分组成的布尔表达式(例如,A && B,C || D,等等)。
解释:在Nejmeh的团队中,每一个单独的例程都有一个取值为200的非正式的NPATH限值;超过这个限值的函数可能会进行进一步的分解,或者至少一探究竟。
检查数组定义的风格。有的开发者使用Java风格:public static void main(String[] args);有的开发者使用C风格:public static void main(String args[])。
限制使用 Unicode转义(例如\ u221e)。容许将转义用于 不可打印(控制)字符。
此外,此检查能够配置为容许使用转义,若是跟踪评论存在。经过该选项,若是文字仅包
含它们,则能够容许使用转义。
控制注释和周围代码之间的缩进。评论的缩写与周围的代码相同。
检查在其余标记之下的受限标记。
警告:这是一项很是强大和灵活的检查,可是与此同时,它偏向于底层技术,而且很是依赖于具体实现,由于,它的结果依赖于咱们用来构建抽象语法树的语法。所以,当其余检查项目提供了你想要用的功能时,咱们建议你使用这些检查项目。总之,这项检查只能在抽象语法树的层面上工做,它并不了解任何语言结构。
检查方法,构造函数和catch块的参数是否为final。不检查接口,抽象和本机方法:final关键字对于接口,抽象和本机方法参数没有意义,由于没有能够修改参数的代码。
理由:在执行方法的算法时更改参数的值可能会使人困惑,应该避免。让Java编译器阻止这种编码风格的一个好办法就是声明参数final。
检查方法/构造器的参数是不是final的。这项检查会忽略接口方法的检查 —— final关键字不会理会接口方法的参数,由于没有任何代码可以修改这些参数。
解释:在方法算法的执行期间改变参数值会使读者产生混淆,所以应当避免这种状况的发生。有个很好的方法可使得Java的编译器预防这种编码风格,那就是将方法的参数声明为final的。
检查Java代码的缩进是否正确。
尽管有些格式精美的打印机有时能够很方便地批量重排原始代码的格式,可是它们一般不是没有足够的可配置性,就是不可以达到预期的排版格式。有时这是我的喜爱的问题,有时这是实际经验的问题。不管如何,这项检查应当只确保代码遵照缩进规则的一个最小集合。
检查文件是否以新行结束。
解释:一般,任何源码文件和文本文件都应当以一个新行符结束,特别是使用诸如CVS这样的SCM系统时。当文件没有以新行结束时,CVS甚至会打印出一个警告。
检查外部类型名称是否与文件名称匹配。例如,类Foo必须在文件Foo.java中。
这项检查负责TODO注释的检查。实际上,这是一种检查Java注释的通用正则表达式匹配器。想要检查其余格式的Java注释,那么设置format属性便可。
这项检查能够确保代码中含有注释的行中只包含注释。在使用//注释的场合下,这意味着//符号以前只能有空格。若是行不是以注释结束的,那么就不会检查这些注释。例如,下面的代码是可接受的
Thread.sleep( 10 );
format属性会处理“} // while”这样的注释。
解释:Steve McConnell编写的《Code Complete》认为行尾注释是一个很差的编程习惯。行尾注释就是那些和实际代码位于同一行的注释。例如:
a = b + c; // 一条常见的注释
d = e / f; // 这一行的另外一条注释
《Code Complete》为此给出了如下几条论证:
1. 注释必须对齐,这样便不会干扰代码的可视结构。若是你不将它们整洁地对齐,它们将会使你的代码看起来就像刚从洗衣机里出来同样乱糟糟的。
2. 行尾注释会很难格式化,须要花费大量的时间来对齐它们。这样的时间并非花在深刻理解代码上的,你只能乏味地敲击空格键或制表键来从新格式化这些注释。
3. 行尾注释很是难以维护。若是某一行包含行尾注释的代码增长了,就会使这行的注释被挤得更远,而且全部其余的行尾注释为了排版对齐,不得不被放置的一样远。难以维护的代码风格就是不可维护的。
4. 行尾注释可能会意义不明确。每行的右侧一般不能提供足够的空间来放置注释,将注释和代码放在同一行就意味着注释可能会比较短。按照这种习惯,你编写代码时就会专一于每行尽量的短,而不是每行尽量的清晰。所以,这种注释常常会意义不明确清晰。
5. 行尾注释还会带来一个系统性问题,你会发现很难仅仅在一行中写出意义明确的注释。大多数行尾注释仅仅重复了一下这行的代码,这种行为带来的危害性远比带来的帮助要大。
当使用自动化重构技术时,源码每行的长度会常常变化,这就使得包含大量行尾注释的代码变得很是难以维护。
这是一项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.
检查源码中是否有未注释的main()方法(调试的残留物)。
解释:调试时常常会在代码中利用main()方法。当调试结束时,开发者常常会忘记删除这些main()方法,这样会改变API,而且会增大生成的class/jar文件的尺寸。除了程序真正的入口点以外,源码中其余全部的main()方法都应当被删除或注释掉。
检查重复属性的属性文件。
原理:多个属性键一般在合并或从新分支后出现。虽然运行时没有错误,可是因为具备不一样的重复属性值,可能会致使混淆。
检查long类型的常量在定义时是否由大写的“L”开头。注意,是“L”,不是“l”。这是由《Java Language Specification》的第3.10.1章节所建议的编码规约。
解释:小写字母“l”看起来很是像数字“1”。
检查代码中的标识符的顺序是否符合《Java Language Specification》中的第8.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
在如下部分检查是否有多余的修饰符:
1. 接口和注解的定义;
2. final类的方法的final修饰符;
3. 被声明为static的内部接口声明。
解释:《Java Language Specification》强烈不建议在接口定义中使用“public”和“abstract”来声明方法。
接口中的变量和注解默认就是public、static、final的,所以,这些修饰符也是多余的。
由于注解是接口的一种形式,因此它们的字段默认也是public、static、final的,正如它们的注解字段默认是public和abstract的。
定义为final的类是不能被继承的,所以,final类的方法的final修饰符也是多余的。
//TODO...
检查抽象类的名称是否遵照命名规约。
//TODO...
检查类的类型参数名称是否遵照命名规约。
检查常量(用static final修饰的字段)的名称是否遵照命名规约。
//TODO...
检查局部final变量的名称是否遵照命名规约。
检查局部变量的名称是否遵照命名规约。
检查成员变量(非静态字段)的名称是否遵照命名规约。
检查方法名称是否遵照命名规约。
检查方法的类型参数名称是否遵照命名规约。
检查包名称是否遵照命名规约。
检查参数名称是否遵照命名规约。
检查静态变量(用static修饰,但没用final修饰的字段)的名称是否遵照命名规约。
检查类的名称是否遵照命名规约。
//TODO...
检查多行是否匹配一条给定的正则表达式。能够处理任何文件类型。
解释:这项检查能够做为原型检查使用,可以发现常见的编码坏习惯,例如调用ex.printStacktrace()、System.out.println()、System.exit(),等等。
//TODO...
检查单行是否匹配一条给定的正则表达式。能够处理任何文件类型。
解释:这项检查能够做为原型检查使用,可以发现常见的编码坏习惯,例如调用ex.printStacktrace()、System.out.println()、System.exit(),等等。
这项检查是RegexpSingleline的变种,用于检测Java文件中的单行是否匹配给定的正则表达式。它支持经过Java注释抑制匹配操做。
检查匿名内部类的长度。
解释:若是一个匿名内部类变得很是长,那么就很难读懂它,而且难以跟踪方法的流程。所以,过长的匿名内部类一般应当被重构为一个具名内部类。能够参考Bloch编写的《Effective Java》的第93页。
将可执行语句的数量限制为一个指定的限值。
检查源码文件的长度。
解释:若是一个源码文件变得很是长,那么就会难以理解。所以,过长的类一般应当被重构成若干个较小的独立的类,每一个类专一于一个特定的任务。
检查源码每行的长度。
解释:将源码打印出来,或者开发者限制了屏幕显示源码的空间(例如,IDE会显示额外的信息,诸如项目树、类层次等等),那么过长的行会显得难以阅读。
检查每一个类型中声明的方法的数量。
它包括了每种可见范围方法的总数(private、package、protected、public)。
检查方法和构造器的长度。
解释:若是一个方法变得很是长,那么就会难以理解。所以,过长的方法一般应当被重构成若干个较小的独立的方法,每一个方法专一于一个特定的任务。
检查在一个文件的外层(或根层)中声明的类型的数量。
解释:通常认为给每一个文件只定义一个外层类型是较好的作法。
检查一个方法或构造器的参数的数量。
检查空的for循环初始化语句的填充符,也就是空格是否能够做为for循环初始化语句空位置的填充符。若是代码自动换行,则不会进行检查,正如如下代码所示:
for (
; i < j; i++, j--)
检查空的for循环迭代器的填充符,也就是空格是否能够做为for循环迭代器空位置的填充符。若是代码自动换行,则不会进行检查,正如如下代码所示:
for (Iterator foo = very.long.line.iterator();
foo.hasNext();
)
//TODO...
检查源码中没有制表符('\t')。
解释:
1. 为了可以更方便地阅读源码,开发者不该当在他们的文本编辑器中配置制表符的宽度。
2. 根据Apache Jakarta的编码标准:在一个分布式开发环境中,当提交的消息被发送到一个邮件列表中时,若是你使用了制表符,那么这些消息会变得几乎不可能阅读。
检查范型标记<和>的周围的空格是否遵照标准规约。这项规约是不可配置的。
例如,如下代码都是合法的:
List x = new ArrayList();
List> y = new ArrayList>();
可是下面的示例代码是不合法的:
List < Integer > x = new ArrayList < Integer > ();
List < List < Integer > > y = new ArrayList < List < Integer > > ();
检查方法定义、构造器定义、方法调用、构造器调用的标识符和参数列表的左圆括号之间的填充符。也就是,若是标识符和左圆括号位于同一行,那么就检查标识符以后是否须要紧跟一个空格。若是标识符和左圆括号不在同一行,那么就报错,除非将规则配置为容许使用换行符。想要在标识符以后使用换行符,将allowLineBreaks属性设置为true便可。
//TODO...
检查指定标记以后没有空格。若要禁用指定标记以后的换行符,将allowLineBreaks属性设为false便可。
检查指定标记以前没有空格。若要容许指定标记以前的换行符,将allowLineBreaks属性设为true便可。
检查代码自动换行时,运算符所处位置的策略。nl表示运算符必须在新行中,eol表示运算符必须在当前行的行末。
检查圆括号的填充符策略,也就是在左圆括号以后和右圆括号以前是否须要有一个空格。
//TODO...
//TODO...
检查类型转换的圆括号的填充符策略。也就是,在左圆括号以后和右圆括号以前是否须要有一个空格。
检查指定标记以后是否紧跟了空格。
检查指定标记的周围是否有空格。如下形式的空构造器和方法的代码体(代码块):
public MyClass() {} // 空构造器
public void func() {} // 空方法
能够选择性地从检查策略中排除,经过设置allowEmptyMethods和allowEmptyConstructors属性便可。
每一个checkstyle配置的根模块。不能被删除。
FileSetCheck TreeWalker会检查单个的Java源码文件,而且定义了适用于检查这种文件的属性。
SeverityMatchFilter过滤器会根据事件的严重级别决定是否要接受审计事件。
SuppressionCommentFilter过滤器使用配对的注释来抑制审计事件。
解释:有时候,违反一项检查是有正当理由的。当问题出在代码自己,而不是我的喜爱时,最好在代码中覆盖检查策略。半结构化的注释能够和检查关联起来。这种作法有时比一个独立的策略抑制文件更好,由于这个文件必须随着源码文件的变化而保持更新。
在检查错误时,SuppressionFilter过滤器会依照一个XML格式的策略抑制文件,选择性地拒绝一些审计事件。若是没有配置好的策略抑制文件可用,那么这个过滤器会接受全部的审计事件。
//TODO...
SuppressWithNearbyCommentFilter过滤器使用独立的注释来抑制审计事件。 解释:和SuppressionCommentFilter相同。然而,SuppressionCommentFilter使用配对的过滤器来打开/关闭注释匹配,SuppressWithNearbyCommentFilter则使用单个注释过滤器。这样可使用更少的行数来标记一块区域,在某些环境中会显得风格很优美。 用法:这个过滤器须要和FileContentsHolder协同使用,由于检查器能够在.java文件中不公开地使用抑制注释。包含这个过滤器的配置文件必须将FileContentsHolder配置为TreeWalker的一个子模块。