一、 Findbugs
1. 安装版本
GUI和作为 Eclipse 插件
2. 安装方法
到官方网站下载最新版本FindBugs http://findbugs.sourceforge.net/downloads.html将 edu.umd.cs.findbugs.plugin.eclipse_1.3.9.20090821.zip下载后解压,然后把解压文件夹复制到 Eclipse安装目录下plugins目录中,重启Eclipse。
3. 特点
检查类获jar文件,它基于bugpatterns,将字节码与缺陷模式进行对比。常使用Visitor模式[1]进行分析。
4. 学习重点
理解Findbugs检测器发现的经典问题及其原理。
5. 缺陷清单
http://findbugs.sourceforge.net/bugDescriptions.html
5.缺陷
Hash equals 不匹配
实例1:重写equals
public classPerson {
Stringname;
int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
public boolean equals(Object person){
if(person !=null){
Personp = (Person)person;
if (p.name.equals(name) && p.age ==age){
return true;
}
}
return false;
} public staticvoidmain(String[] args){
PersonBob1 = newPerson("Bob",22);
PersonBob2 = newPerson("Bob",22);
if(Bob1.equals(Bob2)){
System.out.println("equals Bob1 = Bob2");
}
if(Bob1.hashCode() ==Bob2.hashCode()){
System.out.println("hashCode Bob1 = Bob2");
}
HashMap<Person,Integer> map = new HashMap<Person, Integer>();
map.put(Bob1,123);
if(map.get(Bob2) !=null){
System.out.println("Bob’s value = "+ map.get(Bob2));
}
else{
System.out.println("Not exist");
}
}
}
输出: equals Bob1 = Bob2
Not existNot exist
描述:重写对象的equals()方法,但是没有重写它的 hashCode方法,或者相反的情况时。
解析:equals被重写时,通常有必要重写hashcode 方法,以维护hashcode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。否则,重写equals对象针对内容进行比较,但内容相同的对象hashcode不同,若将一个对象作为hashmap键值,无法查找equals比较结果相等的其他对象。
实例2:重写equals 和 hashCode
public boolean equals(Person p){
if(p !=null){
if (p.name.equals(name) && p.age ==age){
return true;
}
}
return false;
} public inthashCode(){
return age*name.hashCode();
}
输出: equals Bob1 = Bob2
hashCode Bob1 = Bob2
Not exist
描述:定义一个co-variant 版本的 equals()或 compareTo()方法。
解析:Bob类定义其 equals()方法为布尔 equals(Bob),它覆盖了对象中定义的 equals()方法。因为 Java 代码在编译时解析重载方法的方式,在运行时使用的几乎总是在对象中定义的这个版本的方法,而不是在 Bob中定义的那一个(除非显式将 equals()方法的参数强制转换为 Bob类型)。因此,当这个类的一个实例放入到类集合中的任何一个中时,使用的是 Object.equals()版本的方法,而不是在 Bob中定义的版本。在这种情况下, Bob类应当定义一个接受类型为 Object的参数的 equals()方法。
实例3:重写equals 和 hashCode(正确)
public boolean equals(Object person){
if(person !=null){
Personp = (Person)person;
if (p.name.equals(name) && p.age ==age){
return true;
}
}
return false;
}
public int hashCode(){
return age*name.hashCode();
}
输出: equals Bob1 = Bob2
hashCode Bob1 = Bob2
Bob's value = 123
忽略方法返回值
实例1:
package com;
public class Person {
public static void main(String[] args){
StringaString = "bob";
aString.replace('b', 'p');
if(aString.equals("pop")){
System.out.println("This is pop");
}
else{
System.out.println("This is not pop");
}
}
}
输出: This is not pop
描述:忽略方法中返回语句获未将函数返回值赋给变量。
解析:程序员认为他已经用 p 替换了字符串中的所有 b。但所有这类方法都返回一个新字符串,而从来不会改变消息的接收者。新产生的字符串即b.replace('b', 'p'); 的返回值未被赋给新字符串。
实例2:更改为
package com;
public class Person {
public staticvoidmain(String[] args){
String aString = "bob";
String b = aString.replace('b','p');
if(b.equals("pop")){
System.out.println("This is pop");
}
else{
System.out.println("This is not pop");
}
}
}
输出: This is pop
Null指针对null的解引用(dereference)和冗余比较
实例1:
package com;
import java.util.HashMap;
public class Person {
void eat(){
System.out.println("eat ok");
}
void drink(){
System.out.println("drink ok");
}
public staticvoidmain(String[] args){
HashMap<Integer,Person>map = new HashMap<Integer,Person> ();
Person person = map.get(123);
if (person !=null){
person.eat();
}
person.drink();
}
}
输出:Exception in thread "main"java.lang.NullPointerException
at com.Person.main(Person.java:20)
描述:代码路径将会或者可能造成 null 指针异常。null 的冗余比较。
解析:如果两个比较值都为 null,那么它们就是冗余的并可能表明代码错误。若person等于null,则最后一行null 指针异常。
实例2:更改为
public static void main(String[] args){
PersonBob = newPerson();
HashMap<Integer,Person>map = newHashMap<Integer,Person>();
map.put(123,Bob);
Personperson = map.get(123);
if (person !=null) {
person.eat();
}
try{
person.drink();
}catch(Exception e){
e.printStackTrace();
}
}
输出:
eat ok
drink ok
初始化之前读取字段
实例:
package com;
import java.util.List;
import java.util.LinkedList;
import java.util.StringTokenizer;
public classPerson {
private List<String>actions;
public Person(StringstartingActions) {
StringTokenizertokenizer = newStringTokenizer(startingActions);
while(tokenizer.hasMoreTokens()) {
Strings = tokenizer.nextToken();
actions.add(s);
System.out.println(s);
}
}
public static void main(String[] args){
new Person("This is Bob");
}
}
解析:将产生一个 null 指针异常,因为变量 actions还没有初始化。
实例2:更改为
private List<String> actions = newLinkedList<String>();
二、 PMD
1. 安装版本
Eclipse 插件版本
2. 安装方法
1. 在Eclipse中, 点击Help -> Install New Software...
2. 点击 Add..
3. 输入:
Name: PMD for EclipseUpdate Site
URL: http://sourceforge.net/projects/pmd/files/pmd-eclipse/update-site/
4. 点击OK,Next。重启Eclipse。
3. 特点
可以做到检查Java代码中是否含有未使用的变量、是否含有空的抓取块、是否含有不必要的对象等。该软件功能强大,扫描效率高,是Java程序员debug的好帮手。
4. 实例
package com;
import java.util.HashMap;
public classPerson {
Stringname;
int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
public boolean equals(Object person)
{
if(person !=null){
Person p = (Person)person;
if (p.name.equals(name) && p.age ==age){
return true;
}
}
return false;
}
public int hashCode()
{
return age*name.hashCode();
}
public static void main(String[] args){
PersonBob1 = new Person("Bob",22);
PersonBob2 = new Person("Bob",22);
if(Bob1.equals(Bob2)){
System.out.println("equals Bob1 = Bob2");
}
if(Bob1.hashCode() ==Bob2.hashCode()){
System.out.println("hashCode Bob1 = Bob2");
}
HashMap<Person,Integer> map = new HashMap<Person, Integer>();
map.put(Bob1,123);
if(map.get(Bob2) !=null){
System.out.println("Bob's value = "+ map.get(Bob2));
}
else{
System.out.println("Not exist");
}
}
}
5. 规则
规则:变量命名VariableNamingConventions
描述:
A variable naming conventions rule -customize this to your liking. Currently, it
checks for final variables that should befully capitalized and non-final variables
that should not include underscores.
修正:
Person bob1 = new Person("Bob",22);
Personbob 2= newPerson("Bob",22);
规则:短变量ShortVariable
描述:
Fields, local variables, or parameter namesthat are very short are not helpful to the reader.
修正:
Person personA = (Person)person;
规则:判断顺序混乱ConfusingTernary
描述:
Avoid negation within an"if" expression with an "else" clause. For example, rephrase:
if (x != y) diff(); else same();
as:
if (x == y) same(); else diff();
修正:
if(map.get(bob2) ==null){
System.out.println("Not exist");
}
else{
System.out.println("Bob's value = " + map.get(bob2));
}
二、 CheckStyle
1. 安装版本
Eclipse 插件版
2. 安装方法
1. 在Eclipse中, 点击Help -> Install New Software...
2. 点击 Add..
3. 输入:
Name: CheckStyle
URL: http://eclipse-cs.sourceforge.net/update
4. 点击OK,Next。重启Eclipse。
3. 特点
Checkstyle是一款检查Java程序源代码样式的工具,它可以有效的帮助我们检视代码以便更好的遵循代码编写标准,特别适用于小组开发时彼此间的样式规范和统一。
4. 实例
package com;
import java.util.HashMap;
public classPerson {
Stringname;
int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
public boolean equals(Object person)
{
if(person !=null){
Person p = (Person)person;
if (p.name.equals(name) && p.age ==age){
return true;
}
}
return false;
}
public int hashCode()
{
return age*name.hashCode();
}
public static void main(String[] args){
PersonBob1 = new Person("Bob",22);
PersonBob2 = new Person("Bob",22);
if(Bob1.equals(Bob2)){
System.out.println("equals Bob1 = Bob2");
}
if(Bob1.hashCode() ==Bob2.hashCode()){
System.out.println("hashCode Bob1 = Bob2");
}
HashMap<Person,Integer> map = new HashMap<Person, Integer>();
map.put(Bob1,123);
if(map.get(Bob2) !=null){
System.out.println("Bob's value = "+ map.get(Bob2));
}
else{
System.out.println("Not exist");
}
}
}
5. 规范
规范:左括号前留有空格
- '{' is not precededwith whitespace.
规范:多余空行
Linehas trailing spaces.
规范:缺少package-info.java文件
Missing package-info.java file.
测试用例参考文章:www.ibm.com/developerworks/cn/java/j-findbug1/index.html
非常感谢