工作之中遇到了JAVA静态检查相关内容,在此以作总结。
目录
- 静态检查概念
- 静态检查理论基础和应用技术
- 主流Java静态分析工具
- 三者区别
- 三者内置编程规范
- 测试对比
- 总结
静态检查概念
静态代码分析是指无需运行被测代码,仅通过分析或检查源程序的语法、
结构、过程、接口等来检查程序的正确性,找出代码隐藏的错误和缺陷,
如参数不匹配,有歧义的嵌套语句,错误的递归,非法计算,可能出现的空指针引用等等。
静态检查理论基础和应用技术
缺陷模式匹配:
事先从代码分析经验中收集足够多的共性缺陷模式,将待分析代码与已有的共
性缺陷模式进行模式匹配,从而完成软件的安全分析。
这种方式的优点是简单方便,但要求内置足够多缺陷模式,且易产生误报。
类型推断:
指通过对代码中运算对象类型进行推理,从而保证代码中每条语句都针对正确的类型执行。
首先将预定义一套类型机制,包括类型等价等推理规则,而后基于这一规则进行推理计算。
类型推断可以检查代码中的类型错误,简单,高效,适合代码缺陷的快速检测。
模型检查:
建立于有限状态自动机的概念基础之上,这一理论将被分析代码抽象为一个自动机系统,
并且假设该系统是有限状态的、或者是可以通过抽象归结为有限状态。模型检验过程中,
首先将被分析代码中的每条语句产生的影响抽象为一个有限状态自动机的一个状态,
而后通过分析有限状态机从而达到代码分析的目的。
模型检验主要适合检验程序并发等时序特性,但是对于数据值域数据类型等方面作用较弱。
数据流分析:
也是一种软件验证技术,这种技术通过收集代码中引用到的变量信息,从而分析变量在程序中
的赋值、引用以及传递等情况。对数据流进行 分析可以确定变量的定义以及在代码中被引用的
情况,同时还能够检查代码数据流异常,如引用在前赋值在后、只赋值无引用等。
数据流分析主要适合检验程序中的 数据域特性。
主流Java静态分析工具:checkstyle,findbugs,PMD
三者区别
工具 | 分析对象 | 侧重 | 应用技术 |
---|---|---|---|
Checkstyle | 源文件 | 格式缺陷 | 缺陷模式匹配 |
FindBugs | 字节码 | 代码缺陷 | 缺陷模式匹配;数据流分析 |
PMD | 源代码 | 代码缺陷 | 缺陷模式匹配 |
三者内置编程规范
Checkstyle:
Javadoc 注释 | 检查类及方法的Javadoc注释 |
命名约定 | 检查命名是否符合命名规范 |
标题 | 检查文件是否以某些行开头 |
Import语句 | 检查Import语句是否符合定义规范 |
代码块大小 | 检查类、方法等代码块的行数 |
空白 | 检查空白符,如tab,回车符等 |
修饰符 | 修饰符号的检查,如修饰符的定义顺序 |
块 | 检查是否有空块或无效块 |
代码问题 | 检查重复代码,条件判断,魔数等问题 |
类设计 | 检查类的定义是否符合规范,如构造函数的定义等问题 |
FindBugs:
Bad practice坏的实践 | 常见代码错误,用于静态代码检查时进行缺陷模式匹配 |
Correctness可能导致错误的代码 | 如空指针引用等 |
国际化相关问题 | 如错误的字符串转换 |
可能受到的恶意攻击 | 如访问权限修饰符的定义等 |
多线程的正确性 | 如多线程编程时常见的同步,线程调度问题。 |
运行时性能问题 | 如由变量定义,方法调用导致的代码低效问题。 |
PMD:
可能的Bugs | 检查潜在代码错误,如空 try/catch/finally/switch 语句 |
未使用代码(Dead code) | 检查未使用的变量,参数,方法 |
复杂的表达式 | 检查不必要的 if 语句,可被 while 替代的 for 循环 |
重复的代码 | 检查重复的代码 |
循环体创建新对象 | 检查在循环体内实例化新对象 |
资源关闭 | 检查 Connect,Result,Statement 等资源使用之后是否被关闭掉 |
测试对比
测试代码段:
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块
}
}
}
代码缺陷分类 | 示例 | Checkstyle | FindBugs | PMD |
---|---|---|---|---|
引用操作 | 空指针引用 | √ | √ | √ |
对象操作 | 对象比较(用==非equals) | × | √ | √ |
表达式复杂化 | 多余的if语句 | × | × | √ |
数组使用 | 数组下标越界 | × | × | × |
未使用变量或代码段 | 未使用变量 | × | √ | √ |
资源回收 | I/O未关闭 | × | √ | × |
方法调用 | 未使用方法返回值 | × | √ | × |
代码设计 | 空的try/catch/finally块 | × | × | √ |
总结--应结合实际使用
1.侧重不同:
Checkstyle 侧重代码编写格式,编码规范的检验,对代码bug的发现较弱。
FindBugs,PMD着重于发现代码缺陷,针对的代码缺陷类别也各有不同。
2.功能重叠:
在对代码缺陷检查中,几种工具之间功能也有重叠。