`
isiqi
  • 浏览: 16565214 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

转:FindBugs,第 1 部分: 提高代码质量

阅读更多
FindBugs,第 1 部分: 提高代码质量
使用 FindBugs 的原因和方法
静态分析工具承诺无需开发人员费劲就能找出代码中已有的缺陷。当然,如果有多年的编写经验,就会知道这些承诺并不是一定能兑现。尽管如此,好的静态分析工具仍然是工具箱中的无价之宝。在这个由两部分组成的系列文章的第一部分中,高级软件工程师 Chris Grindstaff 分析了 FindBugs 如何帮助提高代码质量以及排除隐含的缺陷。
代码质量工具的一个问题是它们容易为开发人员提供大量但并非真正问题的问题——伪问题(false positives。出现伪问题时,开发人员要学会忽略工具的输出或者放弃它。FindBugs 的设计者 David Hovemeyer William Pugh 注意到了这个问题,并努力减少他们所报告的伪问题数量。与其他静态分析工具不同,FindBugs 不注重样式或者格式,它试图只寻找真正的缺陷或者潜在的性能问题。
FindBugs 是什么?
FindBugs 是一个静态分析工具,它检查类或者 JAR 文件,将字节码与一组缺陷模式进行对比以发现可能的问题。有了静态分析工具,就可以在不实际运行程序的情况对软件进行分析。不是通过分析类文件的形式或结构来确定程序的意图,而是通常使用 Visitor 模式(请参阅 参考资料)。图 1 显示了分析一个匿名项目的结果(为防止可怕的犯罪,这里不给出它的名字):
1. FindBugs UI
让我们看几个 FindBugs 可以发现的问题。
下面的列表没有包括 FindBug 可以找到的所有问题。相反,我侧重于一些更有意思的问题。
检测器:找出 hash equals 不匹配
这个检测器寻找与equals()hashCode()的实现相关的几个问题。这两个方法非常重要,因为几乎所有基于集合的类—— ListMapSet 等都调用它们。一般来说,这个检测器寻找两种不同类型的问题——当一个类:
  • 重写对象的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还没有初始化。
这些例子只是 FindBugs 所发现的问题种类的一小部分(更多信息请参阅 参考资料)。在撰写本文时,FindBugs 提供总共 35 个检测器。
开始使用 FindBugs
要运行 FindBugs,需要一个版本 1.4 或者更高的 Java Development Kit JDK),尽管它可以分析由老的 JDK 创建的类文件。要做的第一件事是下载并安装最新发布的 FindBugs——当前是 0.7.1 (请参阅 参考资料)。幸运的是,下载和安全是相当简单的。在下载了 zip 或者 tar 文件后,将它解压缩到所选的目录中。就是这样了——安装就完成了。
安装完后,对一个示例类运行它。就像一般文章中的情况,我将针对 Windows 用户进行讲解,并假定那些 Unix 信仰者可以熟练地转化这些内容并跟进。打开命令行提示符号并进入 FindBugs 的安装目录。对我来说,这是 C:\apps\FindBugs-0.7.3
FindBugs 主目录中,有几个值得注意的目录。文档在 doc 目录中,但是对我们来说更重要的是,bin 目录包含了运行 FindBugs 的批处理文件,这使我们进入下一部分。
运行 FindBugs
像如今的大多数数工具一样,可以以多种方式运行 FindBugs—— GUI、从命令行、使用 Ant、作为 Eclipse 插件程序和使用 Maven。我将简要提及从 GUI 运行 FindBugs,但是重点放在用 Ant 和命令行运行它。部分原因是由于 GUI 没有提供命令行的所有选项。例如,当前不能指定要加入的过滤器或者在 UI 中排除特定的类。但是更重要的原因是我认为 FindBugs 最好作为编译的集成部分使用,而 UI 不属于自动编译。
使用 FindBugs UI
使用 FindBugs UI 很直观,但是有几点值得说明。如 1所示,使用 FindBugs UI 的一个好处是对每一个检测到的问题提供了说明。图 1 显示了缺陷 Naked notify in method的说明。对每一种缺陷模式提供了类似的说明,在第一次熟悉这种工具时这是很有用的。窗口下面的 Source code 选项卡也同样有用。如果告诉 FindBugs 在什么地方寻找代码,它就会在转换到相应的选项卡时突出显示有问题的那一行。
值得一提的还有在将 FinBugs 作为 Ant 任务或者在命令行中运行 FindBugs 时,选择xml作为ouput选项,可以将上一次运行的结果装载到 UI 中。这样做是同时利用基于命令行的工具和 UI 工具的优点的一个很好的方法。
FindBugs 作为 Ant 任务运行
让我们看一下如何在 Ant 编译脚本中使用 FindBugs。首先将 FindBugs Ant 任务拷贝到 Ant lib 目录中,这样 Ant 就知道新的任务。将 FIND_BUGS_HOME\lib\FindBugs-ant.jar 拷贝到 ANT_HOME\lib
现在看看在编译脚本中要加入什么才能使用 FindBugs 任务。因为 FindBugs 是一个自定义任务,将需要使用taskdef任务以使 Ant 知道装载哪一个类。通过在编译文件中加入以下一行做到这一点:
<taskdef name="FindBugs" classname="edu.umd.cs.FindBugs.anttask.FindBugsTask"/>
在定义了taskdef后,可以用它的名字FindBugs引用它。下一步要在编译中加入使用新任务的目标,如清单 4 所示:


清单 4. 创建 FindBugs 目录
1 <target name="FindBugs" depends="compile">
2 <FindBugs home="${FindBugs.home}" output="xml" outputFile="jedit-output.xml">
3 <class location="c:\apps\JEdit4.1\jedit.jar" />
4 <auxClasspath path="${basedir}/lib/Regex.jar" />
5 <sourcePath path="c:\tempcbg\jedit" />
6 </FindBugs>
7 </target>
让我们更详细地分析这段代码中所发生的过程。
1 行:注意target取决于编译。一定要记住处理的是类文件而是源文件,这样使target对应于编译目标保证了 FindBugs 可在最新的类文件运行。FindBugs 可以灵活地接受多种输入,包括一组类文件、JAR 文件、或者一组目录。
2 行:必须指定包含 FindBugs 的目录,我是用 Ant 的一个属性完成的,像这样:
<property name="FindBugs.home" value="C:\apps\FindBugs-0.7.3" />
可选属性output指定 FindBugs 的结果使用的输出格式。可能的值有xmltext或者emacs。如果没有指定outputFile,那么 FindBugs 会使用标准输出。如前所述,XML 格式有可以在 UI 中观看的额外好处。
3 行:class元素用于指定要 FindBugs 分析哪些 JAR、类文件或者目录。分析多个 JAR 或者类文件时,要为每一个文件指定一个单独的class元素。除非加入了projectFile元素,否则需要class元素。更多细节请参阅 FindBugs 手册。
4 行:用嵌套元素auxClasspath列出应用程序的依赖性。这些是应用程序需要但是不希望 FindBugs 分析的类。如果没有列出应用程序的依赖关系,那么 FindBugs 仍然会尽可能地分析类,但是在找不到一个缺少的类时,它会抱怨。与class元素一样,可以在 FindBugs 元素中指定多个auxClasspath元素。auxClasspath元素是可选的。
5 行:如果指定了sourcePath元素,那么path属性应当表明一个包含应用程序源代码的目录。指定目录使 FindBugs 可以在 GUI 中查看 XML 结果时突出显示出错的源代码。这个元素是可选的。
上面就是基本内容了。让我们提前几个星期。
您已经将 FindBugs 引入到了团队中,并运行它作为您的每小时/每晚编译过程的一部分。当团队越来越熟悉这个工具时,出于某些原因,您决定所检测到的一些缺陷对于团队来说不重要。也许您不关心一些类是否返回可能被恶意修改的对象——也许,像 JEdit,有一个真正需要的(honest-to-goodness)、合法的理由调用System.gc()
总是可以选择关闭特定的检测器。在更细化的水平上,可以在指定的一组类甚至是方法中查找问题时,排除某些检测器。FindBugs 提供了这种细化的控制,可以排除或者包含过滤器。当前只有用命令行或者 Ant 启动的 FindBugs 中支持排除和包含过滤器。正如其名字所表明的,使用排除过滤器来排除对某些缺陷的报告。较为少见但仍然有用的是,包含过滤器只能用于报告指定的缺陷。过滤器是在一个 XML 文件中定义的。可以在命令行中用一个排除或者包含开关、或者在 Ant 编译文件中用excludeFilterincludeFilter指定它们。在下面的例子中,假定使用排除开关。还要注意在下面的讨论中,我对 “bugcode”“bug” “detector”的使用具有某种程度的互换性。
可以有不同的方式定义过滤器:
  • 匹配一个类的过滤器。可以用这些过滤器忽略在特定类中发现的所有问题。
  • 匹配一个类中特定缺陷代码(bugcode)的过滤器。可以用这些过滤器忽略在特定类中发现的一些缺陷。
  • 匹配一组缺陷的过滤器。可以用这些过滤器忽略所分析的所有类中的一组缺陷。
  • 匹配所分析的一个类中的某些方法的过滤器。可以用这些过滤器忽略在一个类中的一组方法中发现的所有缺陷。
  • 匹配在所分析的一个类中的方法中发现的某些缺陷的过滤器。可以用这些过滤器忽略在一组方法中发现的特定缺陷。
知道了这些就可以开始使用了。有关其他定制 FindBugs 方法的更多信息,请参阅 FindBugs 文档。知道如何设置编译文件以后,就让我们更详细地分析如何将 FindBugs 集成到编译过程中吧!
FindBugs 集成到编译过程中
在将 FindBugs 集成到编译过程当中可以有几种选择。总是可以在命令行执行 FindBugs,但是您很可能已经使用 Ant 进行编译,所以最自然的方法是使用 FindBugs Ant 任务。因为我们在 如何运行 FindBugs一节中讨论了使用 FindBugs Ant 任务的基本内容,所以现在讨论应当将 FindBugs 加入到编译过程中的几个理由,并讨论几个可能遇到的问题。
为什么应该将 FindBugs 集成到编译过程中?
经常问到的第一个问题是为什么要将 FindBugs 加入到编译过程中?虽然有大量理由,最明显的回答是要保证尽可能早地在进行编译时发现问题。当团队扩大,并且不可避免地在项目中加入更多新开发人员时,FindBugs 可以作为一个安全网,检测出已经识别的缺陷模式。我想重申在一篇 FindBugs 论文中表述的一些观点。如果让一定数量的开发人员共同工作,那么在代码中就会出现缺陷。像 FindBugs 这样的工具当然不会找出所有的缺陷,但是它们会帮助找出其中的部分。现在找出部分比客户在以后找到它们要好——特别是当将 FindBugs 结合到编译过程中的成本是如此低时。
一旦确定了加入哪些过滤器和类,运行 FindBugs 就没什么成本了,而带来的好处就是它会检测出新缺陷。如果编写特定于应用程序的检测器,则这个好处可能更大。
重要的是要认识到这种成本/效益分析只有在不生成大量误检时才有效。换句话说,如果在每次编译时,不能简单地确定是否引入了新的缺陷,那么这个工具的价值就会被抵消。分析越自动化越好。如果修复缺陷意味着必须吃力地分析检测出的大量不相干的缺陷,那么您就不会经常使用它,或者至少不会很好地使用它。
确定不关心哪些问题并从编译中排除它们。也可以挑出确实关注的一小部分检测器并只运行它们。另一种选择是从个别的类中排除一组检测器,但是其他的类不排除。FindBugs 提供了使用过滤器的极大灵活性,这可帮助生成对团队有意义的结果,由此我们进入下一节。
确定用 FindBugs 的结果做什么
可能看来很显然,但是您想不到我参与的团队中有多少加入了类似 FindBugs 这样的工具而没有真正利用它。让我们更深入地探讨这个问题——用结果做什么?明确回答这个问题是困难的,因为这与团队的组织方式、如何处理代码所有权问题等有很大关系。不过,下面是一些指导:
  • 可以考虑将 FindBugs 结果加入到源代码管理(SCM)系统中。一般的经验做法是不将编译工件(artifact)放到 SCM 系统中。不过,在这种特定情况下,打破这个规则可能是正确的,因为它使您可以监视代码质量随时间的变化。
  • 可以选择将 XML 结果转换为可以发送到团队的网站上的 HTML 报告。转换可以用 XSL 样式表或者脚本实现。有关例子请查看 FindBugs 网站或者邮件列表(请参阅 参考资料)。
  • FindBugs 这样的工具通常会成为用于敲打团队或者个人的政治武器。尽量抵制这种做法或者不让它发生——记住,它只是一个工具,它可以帮助改进代码的质量。有了这种思想,在下一部分中,我将展示如何编写自定义缺陷检测器。
结束语
我鼓励读者对自己的代码试用静态分析工具,不管是 FindBugsPMD 还是其他的。它们是有用的工具,可以找出真正的问题,而 FindBugs 是在消除误检方面做得最好的工具。此外,它的可插入结构提供了编写有价值的、特定于应用程序的检测器的、有意思的测试框架。在本系列的 2 部分中,我将展示如何编写自定义检测器以找出特定于应用程序的问题。

相关推荐

    Findbugs

    作为一款桌面应用程序,FindBugs提供了直观的用户界面,使得开发者能够方便地集成到他们的开发环境中,对代码进行深度分析,从而提高代码质量和可维护性。 在Java开发过程中,FindBugs可以作为质量保证的重要组成...

    findbugs_3.0.1

    《FindBugs 3.0.1:提升代码质量的静态分析神器》 FindBugs,作为一款强大的静态代码分析工具,一直以来都是开发者们优化代码、预防...对于任何致力于提高代码质量的开发团队来说,FindBugs都是不可或缺的工具之一。

    findbugs1.3.9

    总的来说,FindBugs 1.3.9版本的Eclipse插件是开发者提升代码质量的有力工具。它不仅提供了一种在编码阶段发现错误的有效方式,还通过与Eclipse IDE的紧密集成,使得错误检查成为开发过程中的自然组成部分。通过使用...

    Eclipse FindBugs

    这款插件是Eclipse集成开发环境(IDE)的一部分,可以帮助开发者在编写代码阶段就发现并修复问题,从而提高代码质量和可维护性。 FindBugs通过分析字节码而非源代码来工作,这意味着它甚至可以在没有源代码的情况下...

    findbugs-3.0.1.zip

    总结,FindBugs 3.0.1是Java开发者提高代码质量的有力助手,通过静态分析技术,它能够在编码阶段就帮助我们发现并修复潜在的问题,从而降低软件缺陷的风险。通过阅读"使用说明.txt"并实际操作,我们可以更好地理解和...

    FindBugs完全使用手册

    FindBugs是一款专门用于Java字节码静态分析的工具,其主要功能在于帮助开发人员提高代码质量并发现潜在的缺陷。该工具通过对类文件或JAR文件进行分析,并将其与一系列预设的缺陷模式进行比对,从而识别出可能存在的...

    findbugs最新源代码20081008版本的

    对于想要深入理解静态代码分析和提高代码质量的开发者来说,研究FindBugs的源代码是一次宝贵的实践。 总的来说,20081008版FindBugs源代码的探索不仅有助于理解静态分析技术,还可以让我们领略到软件工程中错误预防...

    Findbugs源码

    FindBugs提供了丰富的Bug类别,如DMI(Design for Maintenance Issues)、PER(Performance Problems)和SEC(Security Vulnerabilities),涵盖了多个方面的代码质量与安全性问题。 在FindBugs-1.3.5版本中,包含...

    findbugs-1.3.9 java代码检查工具以及eclipse插件

    使用FindBugs不仅可以提高代码质量,还可以减少调试时间,因为很多问题在编译阶段就能被发现。此外,FindBugs支持与其他持续集成工具的集成,如Jenkins,可以在代码提交时自动进行检查,确保团队代码的质量标准。 ...

    findbugs-2.0.1

    在这个由两部分组成的系列文章的第一部分中,高级软件工程师 Chris Grindstaff 分析了 FindBugs 如何帮助提高代码质量以及排除隐含的缺陷。代码质量工具的一个问题是它们容易为开发人员提供大量但并非真正问题的问题...

    findbugs2.0插件包

    使用FindBugs 2.0插件可以极大地提高代码质量,减少运行时错误,提高软件的可靠性和可维护性。通过定期运行FindBugs分析,开发团队可以遵循最佳实践,避免常见的编程陷阱,同时提升代码的可读性和可测试性。 总的...

    findbugs插件

    无论是新手还是经验丰富的开发者,都应该充分利用FindBugs这样的工具来提高代码的健壮性和可维护性。通过熟练掌握FindBugs的使用,开发者可以更有效地检测和修复潜在问题,从而提升软件的整体品质。

    findbugs.plugin.eclipse

    Eclipse FindBugs插件就是这样一个强大的工具,它能够帮助开发者在编码阶段找出潜在的错误和不良编程习惯,从而提高代码质量。本文将详细讲解“edu.umd.cs.findbugs.plugin.eclipse_2.0.1.20120712”这一版本的...

    edu.umd.cs.findbugs.plugin.eclipse_2.0.0.20111220.zip

    2. 编程错误和坏味道:FindBugs能够检测到代码中可能存在的错误模式,如使用了过时的方法、未初始化的变量、不必要的转换等,有助于提高代码质量。 3. 分级警告:FindBugs将问题分为不同等级(如B, I, E分别代表Bug...

    FindBugs帮助文档

    FindBugs 是一款强大的静态代码分析工具,它可以帮助开发者提高代码质量,减少潜在的运行时错误。通过与 Eclipse 集成,它成为了开发者日常开发流程中的得力助手,有助于实现更高效、更可靠的软件开发。

    fijava findbugs eclipse version 3.3.

    在Eclipse集成开发环境中,FindBugs插件提供了强大的代码质量检查功能,帮助开发者在编码阶段就能发现并修复问题,从而提高软件的质量和可靠性。本文将详细探讨FindBugs Eclipse插件的3.3版本,以及其配置和使用方法...

    edu.umd.cs.findbugs.plugin.eclipse_1.0.0

    在Eclipse集成开发环境中,FindBugs插件为开发者提供了便捷的代码质量检查功能,让开发者可以在编写代码的同时就对可能出现的问题进行预判和处理。 静态分析是一种无需执行程序即可对其内部结构进行分析的技术。...

    findbugs工具包

    在软件开发过程中,FindBugs通过扫描源代码,帮助开发者提前发现并修复问题,提高代码质量和可靠性。它的核心功能是通过复杂的算法分析代码,找出那些在运行时可能会导致程序异常或行为不正确的模式。 在FindBugs的...

    代码静态检查 PMD findBugs checkStyle

    它们都是持续集成流程中的重要组成部分,可以显著提高代码质量,减少bug的出现,从而降低后期维护成本。 在压缩包中提供的"findBugs缺陷详解.chm"文件,应该是对FindBugs工具进行深入讲解的文档,包含了FindBugs所...

Global site tag (gtag.js) - Google Analytics