在我们平时项目开发中,经常会写一些不严谨的代码
或者一些比较低级的错误代码,但是这些错误往往很难被发现,
这样就导致了我们的项目中会隐藏了很多影响性能
甚至是导致闪退的错误代码,于是许多检测工具就出现了
很多静态扫描工具,比如Google出的针对Android的Lint
还有针对Java的FindBugs,PMD,Checkstyle等等
Lint
使用 Lint 改进代码,Android开发文档中有介绍
https://developer.android.com/studio/write/lint?hl=zh-cn
Android Studio 提供一个名为 Lint 的代码扫描工具,
可帮助发现并纠正代码结构质量的问题,而无需实际执行该应用,
也不必编写测试用例。
该工具会报告其检测到的每个问题并提供该问题的描述消息和严重级别,
以便快速确定需要优先进行哪些关键改进。
此外,可以调低问题的严重级别,忽略与项目无关的问题,
也可以调高严重级别,以突出特定问题。
Lint 工具可检查
- Android 项目源文件是否包含潜在错误,
以及在正确性、安全性、性能、易用性、便利性 - 国际化方面是否需要优化改进。
在使用 Android Studio 时,配置的 Lint 和 IDE 检查会在您每次构建应用时运行。
不过,可以手动运行检查或从命令行运行 Lint
详细的就不介绍了,官网有很详细的解释
FindBugs
FindBug官网:http://findbugs.sourceforge.net/bugDescriptions.html
FindBugs是由Bill Pugh和David Hovemeyer创建的开源程序,
用来查找Java代码中的程序错误。[1][2]它使用静态分析[1][3]来识别Java程序中上百种不同类型的潜在错误。
潜在错误可分为四个等级:恐怖的(scariest)、吓人的(scary)、令人困扰的(troubling)
和值得关注的(of concern),这是根据其可能产生的影响或严重程度,而对开发者的提示。
[4]FindBugs操作的是Java字节码,[3]而非源代码。软件作为一个独立的图形化应用程序分发。
[5] Eclipse[6]、NetBeans[7]、IntelliJ IDEA[8][9][10]、Gradle、Hudson[11]和Jenkins[12]有可用的FindBugs插件。
也可以给FindBugs添加用于检查的规则集
----以上来自维基百科
Gradle 插件的API介绍:
https://docs.gradle.org/current/dsl/org.gradle.api.plugins.quality.FindBugs.html
简单列举如下几个问题:
检测器:找出 hash equals 不匹配
这个检测器寻找与 equals()和 hashCode()的实现相关的几个问题。这两个方法非常重要,
因为几乎所有基于集合的类—— List、Map、Set 等都调用它们。一般来说,
这个检测器寻找两种不同类型的问题——当一个类:
- 重写对象的 equals()方法,但是没有重写它的 hashCode方法,或者相反的情况时。
- 定义一个 co-variant 版本的 equals()或 compareTo()方法。例如, Bob类定义其 equals()方法为
布尔 equals(Bob),它覆盖了对象中定义的 equals()方法。
因为 Java 代码在编译时解析重载方法的方式,
在运行时使用的几乎总是在对象中定义的这个版本的方法,而不是在 Bob中定义的
那一个(除非显式将 equals()方法的参数强制转换为 Bob类型)。因此,当这个类的
一个实例放入到类集合中的任何一个中时,
使用的是 Object.equals()版本的方法,而不是在 Bob中定义的版本。
在这种情况下, Bob类应当定义一个接受类型为 Object的参数的 equals()方法。
检测器:忽略方法返回值
这个检测器查找代码中忽略了不应该忽略的方法返回值的地方。这种情况的一个常见例子是在
调用 String方法时,如在清单 1 中:
清单 1. 忽略返回值的例子
1 String aString = "bob";
2 b.replace('b', 'p');
3 if(b.equals("pop"))
这个错误很常见。在第 2 行,程序员认为他已经用 p 替换了字符串中的所有 b。确实是这样,
但是他忘记了字符串是不可变的。所有这类方法都返回一个新字符串,而从来不会改变消息的接收者。
检测器:Null 指针对 null 的解引用(dereference)和冗余比较
这个检测器查找两类问题。它查找代码路径将会或者可能造成 null 指针异常的情况,
它还查找对 null 的冗余比较的情况。例如,如果两个比较值都为 null,那么它们就是冗余的
并可能表明代码错误。FindBugs 在可以确定一个值为 null 而另一个值不为 null 时,
检测类似的错误,如清单 2 所示:
清单 2. Null 指针示例
1 Person person = aMap.get("bob");
2 if (person != null) {
3 person.updateAccessTime();
4 }
5 String name = person.getName();
在这个例子中,如果第 1 行的 Map不包括一个名为“bob”的人,那么在第 5 行询问 person的名字时
就会出现 null 指针异常。因为 FindBugs 不知道 map 是否包含“bob”,所以它将第 5 行标记为
可能 null 指针异常。
检测器:初始化之前读取字段
这个检测器寻找在构造函数中初始化之前被读取的字段。这个错误通常是——尽管不总是
如此——由使用字段名而不是构造函数参数引起的,如清单 3 所示:
清单 3. 在构造函数中读取未初始化的字段
1 public class Thing {
2 private List actions;
3 public Thing(String startingActions) {
4 StringTokenizer tokenizer = new StringTokenizer(startingActions);
5 while (tokenizer.hasMoreTokens()) {
6 actions.add(tokenizer.nextToken());
7 }
8 }
9 }
在这个例子中,第 6 行将产生一个 null 指针异常,因为变量 actions还没有初始化。
PMD
PMD官网:https://sourceforge.net/projects/pmd/
PMD是一个开源静态源代码分析器,可报告应用程序代码中发现的问题。
PMD包含内置规则集,并支持编写自定义规则的功能。 PMD不报告编译错误,
因为它只能处理格式良好的源文件。 PMD报告的问题是相当低效的代码或糟糕的编程习惯,
如果它们累积,可能会降低程序的性能和可维护性
----以上来自维基百科
PMD 包含 16 个规则集,涵盖了 Java 的各种常见问题,其中一些规则要比其他规则更有争议:
- 基本(rulesets/basic.xml)—— 规则的一个基本合集,可能大多数开发人员都不认同它:
catch 块不该为空,无论何时重写 equals(),都要重写 hashCode(),等等。 - 命名(rulesets/naming.xml)—— 对标准 Java 命令规范的测试:变量名称不应太短;
方法名称不应过长;类名称应当以小写字母开头;方法和字段名应当以小写字母开头,等等。 - 未使用的代码(rulesets/unusedcode.xml)—— 查找从未使用的私有字段和本地变量、
执行不到的语句、从未调用的私有方法,等等。 - 设计(rulesets/design.xml)—— 检查各种设计良好的原则,例如: switch 语句应当有 default 块,
应当避免深度嵌套的 if 块,不应当给参数重新赋值,不应该对 double 值进行相等比较。
导入语句(rulesets/imports.xml)—— 检查 import 语句的问题,比如同一个类被导入两次或者被导入 java.lang 的类中。 - JUnit 测试(rulesets/junit.xml)—— 查找测试用例和测试方法的特定问题,例如方法名称的正确拼写,
以及 suite() 方法是不是 static 和 public。 - 字符串(rulesets/string.xml)—— 找出处理字符串时遇到的常见问题,例如重复的字符串标量,调用 String 构造函数,对 String 变量调用 toString() 方法。
- 括号(rulesets/braces.xml)—— 检查 for、 if、 while 和 else 语句是否使用了括号。
- 代码尺寸(rulesets/codesize.xml)—— 测试过长的方法、有太多方法的类以及重构方面的类似问题。
- Javabean(rulesets/javabeans.xml)—— 查看 JavaBean 组件是否违反 JavaBean 编码规范,比如没有序列化的 bean 类。
- 终结函数(finalizer)—— 因为在 Java 语言中, finalize() 方法不是那么普遍(我上次编写这个代码也经是好多年前的事了),所以它们的使用规则虽然很详细,但是人们对它们相对不是很熟悉。这类检查查找 finalize() 方法的各种问题,例如空的终结函数,调用其他方法的 finalize() 方法,对 finalize() 的显式调用,等等。
- 克隆(rulesets/clone.xml)—— 用于 clone() 方法的新规则。凡是重写 clone() 方法的类都必须实现 Cloneable, clone() 方法应该调用 super.clone(),而 clone() 方法应该声明抛出 CloneNotSupportedException 异常,即使实际上没有抛出异常,也要如此。
- 耦合(rulesets/coupling.xml)—— 查找类之间过度耦合的迹象,比如导入内容太多;在超类型或接口就已经够用的时候使用子类的类型;类中的字段、变量和返回类型过多等。
严格的异常(rulesets/strictexception.xml)—— 针对异常的测试:不应该声明该方法而抛出 java.lang.Exception 异常,不应当将异常用于流控制,不应该捕获 Throwable,等等。 - 有争议的(rulesets/controversial.xml)—— PMD 的有些规则是有能力的 Java 程序员可以接受的。但还是有一些争议。这个规则集包含一些更有问题的检验,其中包括把 null 赋值给变量、方法中有多个返回点,以及从 sun 包导入等。
日志(rulesets/logging-java.xml)—— 查找 java.util.logging.Logger 的不当使用,包括非终状态(nonfinal)、非静态的记录器,以及在一个类中有多个记录器。
IBM Developer PMD:https://www.ibm.com/developerworks/cn/java/j-pmd/index.html
Checkstyle
Checkstyle[1]是在软件开发中的一种静态代码分析工具,用来检查Java源代码是否匹配编码规则。
Checkstyle定义了一组可用的模块,每个模块都提供可配置严格程度的规则(强制性的、可选的等)。
每项规则都可以发出通知、警告和错误。例如,Checkstyle可以检查下列问题:
- 类、属性和方法的Javadoc;
- 属性和方法的命名规范;
- 函数参数数量、代码行的长度的限制;
- 标题是否存在[2];
- 包的导入、类、访问控制修饰符、代码块的使用;
- 字符间的间隔;
- 重复代码[3];
- 代码中多种复杂度的度量
----以上来自维基百科
Checkstyle的规则是支持自定义的,比如大型软件开发公司是可以定义公司自己的规则并导入
主要是从官网或其他地方找了一些关于静态扫描工具的介绍
下次将会介绍如何在Jenkins上配置这些工具