PMD规则
Tom Copeland 的 PMD 是⼀个开源(BSD 许可)⼯具,它分析 Java 源代码,出潜在的 bug。在⼀般意义上来说,它与 FindBugs 和Lint4j 这类⼯具类似。但是,所有这些⼯具出的 bug 各不相同,所以在给定代码基址上把这些⼯具都运⾏⼀遍很有好处。在本⽂中,我将解释如何使⽤ PMD,并展⽰可以从 PMD 中获得什么。本⽂将介绍 PMD 的命令⾏界⾯。您也可以把 PMD 与 Ant 集成在⼀起,以便进⾏⾃动源代码检查,⽽且还可以将 PMD 与⼀些可⽤于⼤多数主要 IDE 和程序员编辑器的插件集成在⼀起。
安装并运⾏ PMD
PMD 是⽤ Java 编程语⾔编写的,并且要求使⽤ JDK 1.3 或更⾼的版本。如果您习惯使⽤命令⾏,那么 PMD 的安装和运⾏会⾮常简单。先下载 zip 压缩⽂件,然后把它解压到合适的位置,⽐如 /usr 或您⾃⼰的主⽬录中。本⽂假设您把它解压到 /usr 中。
运⾏ PMD 最简单的⽅法是调⽤脚本 pmd.sh(在 Unix/Linux 上)或脚本 pmd.bat(在 Windows 上)。不太合常规的是,这些脚本在pmd-2.1/etc ⽬录中,⽽不是在 bin ⽬录中。这个脚本采⽤了三个命令⾏参数:
要检查的 .java ⽂件的路径。
指定输出格式的关键字 html 或 xml。
要运⾏的规则集的名称。
例如,以下命令使⽤命名规则集检查 ImageGrabber.java ⽂件并⽣成 XML 输出:
1 $ /usr/pmd- 2.1/etc/pmd.sh ImageGrabber.java xml l
分析结果
以上命令的输出类似于清单 1 中的报告,如下所⽰,默认情况下,这些输出被发送到 System.out:
清单 1. PMD 的 XML 报告
1 <?xml version=" 1.0"?><pmd>java xml是什么
2 <file name="/Users/elharo/src/ImageGrabber.java">
3 <violation line="32"rule="ShortVariable"
4 ruleset="Naming Rules"priority="3">
5 Avoid variables with short names like j
6 </violation>
7 <violation line="105"rule="VariableNamingConventionsRule"
8 ruleset="Naming Rules"priority="1">
9 Variables that are not final should not contain underscores
10 (except for underscores in standard prefix/suffix).
11 </violation>
12 </file>
13 <error filename="/Users/elharo/src/ImageGrabber.java"
14 msg="Error while processing /Users/elharo/ImageGrabber.java"/>
15 </pmd>
16
在清单 1 中可以看到,PMD 发现了两个问题:在 ImageGrabber.java 的第 32 ⾏有⼀个短变量名称,在第 105 ⾏的名称中有⼀个下划线。这些看起来可能是⼩问题,但是后果却可能是惊⼈的。在这个例⼦中,105 ⾏的下划线只是⼀个有 10 年之久的⽼代码中⼀些容易修正的⼩⽑病。但是仔细考察第⼀个问题,使我认识到可以完全排除 j 变量,因为它与另外⼀个单独递增变量的功能相同。这个程序仍然有⽤,但是它应该更简洁⼀些,以便应对以后的挑战。每当清除⼀⾏代码,也就减少了⼀个可能隐 匿 bug 的地⽅。
您可以把 PMD 的输出重定向到⽂件中,或者通过管道,以常见的⽅式将它传递到编辑器中。我通常更喜欢⽣成 HTML 格式的输出,并将它加载到 Web 浏览器中,如图 1 中所⽰:
图 1. PMD ⽤ HTML 格式处理后的输出
在检查源代码树时,把结果输出到⽂件中会⾮常有帮助。如果把⽬录、zip ⽂件或 JAR 档案⽂件传递给第⼀个参数,那么 PMD 会递归地检查⽬录或档案中的每个 .java ⽂件。输出的总量可能有些吓⼈,特别是在 PMD ⽣成⼤量误报(false positive)的时候。例如,当针对XOM(请参阅 参考资料)代码基址运⾏ PMD 时,它不断地报告应当“Avoid variables with short names like in.”(避免像 in 这样的短变量名)。⽽我却恰恰认为“in”是指向 InputStream 的变量的⾮常好的名称。尽管如此,如果⽤⼀个好的⽂档编辑器来查看输出,您通常会发现,可以很容易地认出并删除最常见的误报,因为它们通常⾮常相似;然后 您就可以解决其余的问题了。
PMD 中惟⼀缺乏的特性就是不能向源代码中添加 “lint 注释”,以便对要执⾏的⼀些明显有危险的操作进⾏提醒。不过,也许这是⼀项特性,⽽不是 bug。只有⼀次,我改变了⾃⼰对真正误报的看法,觉得 PMD 始终是正确的。例如,对于⼀个长时间的 try- catch 块,类似 XOM 中的不同地⽅出现的那个 try-catch 块:
1 try {
2 this .data =Bytes("UTF8");
3 }
4 catch (UnsupportedEncodingException ex) {
5 // All VMs support UTF-8
6 }
PMD 把这标记为空 catch 块。这看起来不是什么问题,但是后来我发现某些虚拟机实际上不认识 UTF-8 编码,尽管这会使它们不符合标准。所以我把这个块修改如下,然后 PMD 开始停⽌报错:
1 try {
2 this .data =Bytes("UTF8");
3 }
4 catch (UnsupportedEncodingException ex) {
5 throw new RuntimeException("Broken VM: Does not support UTF-8");
6 }
可⽤的规则
PMD 包含 16 个规则集,涵盖了 Java 的各种常见问题,其中⼀些规则要⽐其他规则更有争议:
基本(l)—— 规则的⼀个基本合集,可能⼤多数开发⼈员都不认同它: catch 块不该为空,⽆论何时重写
equals(),都要重写 hashCode(),等等。
命名(l)—— 对标准 Java 命令规范的测试:变量名称不应太短;⽅法名称不应过长;类名称应当以⼩写字母开头;⽅法和字段名应当以⼩写字母开头,等等。
未使⽤的代码(l)—— 查从未使⽤的私有字段和本地变量、执⾏不到的语句、从未调⽤的私有⽅法,等等。
设计(l)—— 检查各种设计良好的原则,例如: switch 语句应当有 default 块,应当避免深度嵌套的 if 块,不应当给参数重新赋值,不应该对 double 值进⾏相等⽐较。
导⼊语句(l)—— 检查 import 语句的问题,⽐如同⼀个类被导⼊两次或者被导⼊ java.lang 的类中。
JUnit 测试(l)—— 查测试⽤例和测试⽅法的特定问题,例如⽅法名称的正确拼写,以及 suite() ⽅法是不是 static 和 public。
字符串(l)—— 出处理字符串时遇到的常见问题,例如重复的字符串标量,调⽤ String 构造函数,对 String 变量调⽤ toString() ⽅法。
括号(l)—— 检查 for、 if、 while 和 else 语句是否使⽤了括号。
代码尺⼨(l)—— 测试过长的⽅法、有太多⽅法的类以及重构⽅⾯的类似问题。
Javabean(l)—— 查看 JavaBean 组件是否违反 JavaBean 编码规范,⽐如没有序列化的 bean 类。
终结函数(finalizer)—— 因为在 Java 语⾔中, finalize() ⽅法不是那么普遍(我上次编写这个代码也经是好多年前的事了),所以它们的使⽤规则虽然很详细,但是⼈们对它们相对不是很熟悉。这类检查查 finalize() ⽅法的各种问题,例如空的终结函数,调⽤其他⽅法的finalize() ⽅法,对 finalize() 的显式调⽤,等等。
克隆(l)—— ⽤于 clone() ⽅法的新规则。凡是重写 clone() ⽅法的类都必须实现 Cloneable, clone() ⽅法应该调⽤ super.clone(),⽽ clone() ⽅法应该声明抛出 CloneNotSupportedException 异常,即使实际上没有抛出异常,也要如此。
耦合(l)—— 查类之间过度耦合的迹象,⽐如导⼊内容太多;在超类型或接⼝
就已经够⽤的时候使⽤⼦类的类型;类中的字段、变量和返回类型过多等。
严格的异常(l)—— 针对异常的测试:不应该声明该⽅法⽽抛出 java.lang.Exception 异常,不应当将异常⽤于流控制,不应该捕获 Throwable,等等。
有争议的(l)—— PMD 的有些规则是有能⼒的 Java 程序员可以接受的。但还是有⼀些争议。这个规则集包含⼀些更有问题的检验,其中包括把 null 赋值给变量、⽅法中有多个返回点,以及从 sun 包导⼊等。
⽇志(l)—— 查 java.util.logging.Logger 的不当使⽤,包括⾮终状态(nonfinal)、⾮静态的记录器,以及在⼀个类中有多个记录器。
您可以⼀次⽤多个规则集进⾏检查,只需在命令⾏中⽤逗号分隔规则集名称即可:
1 $ /usr/pmd- 2.1/etc/pmd.sh ~/Projects/XOM/src html
2 l,l,l
构建⾃⼰的规则集
如果频繁地⽤某个规则集合进⾏检查,那么您可能想把它们组合在⾃⼰的规则集⽂件中,如清单 2 所⽰。这个规则集导⼊了⼀些基本规则、命名规则和设计规则:
清单 2. 导⼊基本规则、命名规则和设计规则的规则集
1 <?xml version=" 1.0"?>
2 <ruleset name="customruleset">
3 <description>
4 Sample ruleset for developerWorks article
5 </description>
6 <rule ref="l"/>
7 <rule ref="l"/>
8 <rule ref="l"/>
9 </ruleset>
10
如果您的需求还要细⼀些,那么您可以从每个规则集中选取每个想要包含的规则。例如,清单 3 显⽰了⼀个定制规则集,它从三个内置规则集中选取了 11 个特定的规则。因为检查⼤型的代码基址需要的时间相当长,即使是在快速硬件上也是如此,所以这种⽅法还可以帮助您更快地发现您要查的特定问题。
清单 3. 导⼊ 11 个特定规则的规则集
1 <?xml version=" 1.0"?>
2 <ruleset name="specific rules">
3 <description>
4 Sample ruleset for developerWorks article
5 </description>
6 <rule ref="l/AvoidReassigningParametersRule"/>
7 <rule ref=
8 "l/ConstructorCallsOverridableMethod"/>
9 <rule ref="l/FinalFieldCouldBeStatic"/>
10 <rule ref="l/DefaultLabelNotLastInSwitchStmt"/>
11 <rule ref="l/LongVariable"/>
12 <rule ref="l/ShortMethodName"/>
13 <rule ref="l/VariableNamingConventions"/>
14 <rule ref="l/MethodNamingConventions"/>
15 <rule ref="l/ClassNamingConventions"/>
16 <rule ref="l/EmptyCatchBlock"/>
17 <rule ref="l/EmptyFinallyBlock"/>
18 </ruleset>
19
您也可以把⼤多数规则包含在⼀个集合,但是不包括少数您不同意的或者会造成⼤量误报的特定规则。例如,XOM 在进⾏表查的时候经常使⽤没有 default 块的 switch 语句。我可以保留⼤多数设计规则,但是可以在导⼊设计规则的规则元素中添加 ⼦元素,避开对遗漏default 块的检查,如清单 4 所⽰:
清单 4. 排除了设计规则的规则集, switch 语句应当包含 default 块
1 <?xml version=" 1.0"?>
2 <ruleset name="dW rules">
3 <description>
4 Sample ruleset for developerWorks article
5 </description>
6 <rule ref="l">
7 <exclude name="SwitchStmtsShouldHaveDefault"/>
8 </rule>
9 </ruleset>
10
(但是,细想⼀下,也许 PMD 是对的,⽽我应该添加 default 块。)
您可⽤的规则并不仅限于内置规则。您可以添加新规则:可以通过编写 Java 代码并重新编译 PDM,或者更简单些,编写 XPath 表达式,它会针对每个 Java 类的抽象语法树进⾏处理。
结束语
即使只使⽤内置规则(内容相当全⾯),PMD 也可以到您的代码中的⼀些真正问题。某些问题可能很⼩,但有些问题则可能很⼤。PMD 不可能到每个 bug,您仍然需要做单元测试和接受测试,在查已知 bug 时,即使是 PMD 也⽆法替代⼀个好的调试器。但
是,PMD 确实可以帮助您发现未知的问题。我还没有看到 PMD 不到任何问题的代码基址。这是⼀个便宜、简易、有意思的改进程序的⽅式。如果您以前从未⽤过 PMD,那么您以及您的客户应该试试它
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论