常用 Java 静态代码分析工具的分析与比较


转载自 开源中国社区 http://www.oschina.net/question/129540_23043

     

1月16日厦门 OSC 源创会火热报名中,奖品多多哦 ?  

简介: 本文首先介绍了静态代码分析的基本概念及主要技术,随后分别介绍了现有 4 种主流 Java 静态代码分析工具 (Checkstyle,FindBugs,PMD,Jtest),最后从功能、特性等方面对它们进行分析和比较,希望能够帮助 Java 软件开发人员了解静态代码分析工具,并选择合适的工具应用到软件开发中。

图 1. 使用 Checkstyle 进行编码风格检查 

此外,Checkstyle 支持用户根据需求自定义代码检查规范,在下图 2 中的配置面板中,用户可以在已有检查规范如命名约定,Javadoc,块,类设计等方面的基础上添加或删除自定义检查规范。


图 2. 使用 Checkstyle 添加自定义代码检查规范 

图 3. 使用 FindBugs 进行静态代码分析 

图中 Bug Explorer 中的灰色图标处为 Bug 类型,每种分类下红色图标表示 bug 较为严重,黄色的图标表示 bug 为警告程度。Propreties 列出了 bug 的描述信息及修改方案。

此外,FindBugs 还为用户提供定制 Bug Pattern 的功能。用户可以根据需求自定义 FindBugs 的代码检查条件,如下图 4 所示:


图 4. 使用 FindBugs 添加自定义代码检查规范 

图 5. 使用 PMD 进行静态代码分析 

PMD 同样也支持开发人员对代码检查规范进行自定义配置。开发人员可以在下图 6 中的面板中添加、删除、导入、导出代码检查规范。


图 6. 使用 PMD 添加自定义代码检查规范 

图 7. 使用 Jtest 进行静态代码分析 

同时,Jtest 还提供了对用户定制代码检查配置甚至自定义编码规则的支持,这一功能使得开发人员可以基于不同场景定制所需要的编码规范,如图 8 所示:


图 8. 使用 Jtest 添加自定义代码检查规范

Java 静态分析工具对比

本章节将从以下几个方面对上述 Java 静态分析工具进行比较:

应用技术及分析对象

下表 1 列出了不同工具的分析对象及应用技术对比:


表 1. 不同工具的分析对象及应用技术对比 
Java 静态分析工具分析对象应用技术
Checkstyle Java 源文件 缺陷模式匹配
FindBugs 字节码 缺陷模式匹配;数据流分析
PMD Java 源代码 缺陷模式匹配
Jtest Java 源代码 缺陷模式匹配;数据流分析

内置编程规范

Checkstyle:

  • Javadoc 注释:检查类及方法的 Javadoc 注释
  • 命名约定:检查命名是否符合命名规范
  • 标题:检查文件是否以某些行开头
  • Import 语句:检查 Import 语句是否符合定义规范
  • 代码块大小,即检查类、方法等代码块的行数
  • 空白:检查空白符,如 tab,回车符等
  • 修饰符:修饰符号的检查,如修饰符的定义顺序
  • 块:检查是否有空块或无效块
  • 代码问题:检查重复代码,条件判断,魔数等问题
  • 类设计:检查类的定义是否符合规范,如构造函数的定义等问题

FindBugs:

  • Bad practice 坏的实践:常见代码错误,用于静态代码检查时进行缺陷模式匹配
  • Correctness 可能导致错误的代码,如空指针引用等
  • 国际化相关问题:如错误的字符串转换
  • 可能受到的恶意攻击,如访问权限修饰符的定义等
  • 多线程的正确性:如多线程编程时常见的同步,线程调度问题。
  • 运行时性能问题:如由变量定义,方法调用导致的代码低效问题。

PMD:

  • 可能的 Bugs:检查潜在代码错误,如空 try/catch/finally/switch 语句
  • 未使用代码(Dead code):检查未使用的变量,参数,方法
  • 复杂的表达式:检查不必要的 if 语句,可被 while 替代的 for 循环
  • 重复的代码:检查重复的代码
  • 循环体创建新对象:检查在循环体内实例化新对象
  • 资源关闭:检查 Connect,Result,Statement 等资源使用之后是否被关闭掉

Jtest

  • 可能的错误:如内存破坏、内存泄露、指针错误、库错误、逻辑错误和算法错误等
  • 未使用代码:检查未使用的变量,参数,方法
  • 初始化错误:内存分配错误、变量初始化错误、变量定义冲突
  • 命名约定:检查命名是否符合命名规范
  • Javadoc 注释:检查类及方法的 Javadoc 注释
  • 线程和同步:检验多线程编程时常见的同步,线程调度问题
  • 国际化问题:
  • 垃圾回收:检查变量及 JDBC 资源是否存在内存泄露隐患

错误检查能力

为比较上述 Java 静态分析工具的代码缺陷检测能力,本文将使用一段示例代码进行试验,示例代码中将涵盖我们开发中的几类常见错误,如引用操作、对象操作、表达式复杂化、数 组使用、未使用变量或代码段、资源回收、方法调用及代码设计几个方面。最后本文将分别记录在默认检查规范设置下,不同工具对该示例代码的分析结果。以下为 示例代码 Test.java。其中,代码的注释部分列举了代码中可能存在的缺陷。


清单 1. Test.java 示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 package Test; import java.io.*; public class Test {     /**      * Write the bytes from input stream to output stream.      * The input stream and output stream are not closed.      * @param is      * @param os      * @throws IOException      */     public  boolean copy(InputStream is, OutputStream os) throws IOException {         int count = 0;         //缺少空指针判断         byte[] buffer = new byte[1024];         while ((count = is.read(buffer)) >= 0) {             os.write(buffer, 0, count);         }         //未关闭I/O流         return true;     }     /**      *      * @param a      * @param b      * @param ending      * @return copy the elements from a to b, and stop when meet element ending      */     public void copy(String[] a, String[] b, String ending)     {         int index;         String temp = null;         //空指针错误         System.out.println(temp.length());         //未使用变量         int length=a.length;         for(index=0; index&a.length; index++)         {             //多余的if语句             if(true)             {                 //对象比较 应使用equals                 if(temp==ending)                 {                     break;                 }                 //缺少 数组下标越界检查                 b[index]=temp;             }         }     }     /**      *      * @param file      * @return file contents as string; null if file does not exist      */     public  void  readFile(File file) {         InputStream is = null;         OutputStream os = null;             try {                 is = new BufferedInputStream(new FileInputStream(file));                 os = new ByteArrayOutputStream();                 //未使用方法返回值                 copy(is,os);                 is.close();                 os.close();             } catch (IOException e) {                 //可能造成I/O流未关闭                 e.printStackTrace();             }             finally             {                 //空的try/catch/finally块             }     } }
通过以上测试代码,我们对已有 Java 静态代码分析工具的检验结果做了如下比较,如下表 2 所示。 
表 2. Java 静态代码分析工具对比 
代码缺陷分类示例CheckstyleFindBugsPMDJtest
引用操作 空指针引用
对象操作 对象比较(使用 == 而不是 equals)  
表达式复杂化 多余的 if 语句      
数组使用 数组下标越界      
未使用变量或代码段 未使用变量  
资源回收 I/O 未关闭    
方法调用 未使用方法返回值      
代码设计 空的 try/catch/finally 块      

由表中可以看出几种工具对于代码检查各有侧重。其中,Checkstyle 更偏重于代码编写格式,及是否符合编码规范的检验,对代码 bug 的发现功能较弱;而 FindBugs,PMD,Jtest 着重于发现代码缺陷。在对代码缺陷检查中,这三种工具在针对的代码缺陷类别也各有不同,且类别之间有重叠。

总结

本文分别从功能、特性和内置编程规范等方面详细介绍了包括 Checkstyle,FindBugs,PMD,Jtest 在内的四种主流 Java 静态代码分析工具,并通过一段 Java 代码示例对这四种工具的代码分析能力进行比较。由于这四种工具内置编程规范各有不同,因此它们对不同种类的代码问题的发现能力也有所不同。其中 Checkstyle 更加偏重于代码编写格式检查,而 FindBugs,PMD,Jtest 着重于发现代码缺陷。最后,希望本文能够帮助 Java 软件开发和测试人员进一步了解以上四种主流 Java 静态分析工具,并帮助他们根据需求选择合适的工具。

相关