FindBugs常见错误介绍、分析、处理

FindBugs简单了解

FindBugs-IDEA是个好东西,它是一个静态分析工具,它检查类或者 JAR 文件,将字节码与一组缺陷模式进行对比以发现可能的问题。有了静态分析工具,就可以在不实际运行程序的情况对软件进行分析。不是通过分析类文件的形式或结构来确定程序的意图,而是通常使用 Visitor 模式。

常见findBugs缺陷类型

(1)Bad pratice编程的坏习惯 。主要是命名问题,比如类名最好以大写开头,字符串不要使用等号不等号进行比较,可能会有异常最好用try-catch包裹的代码,方法有返回值但被忽略等等,这些如果不想改可以直接忽略.

(2)Multithreaded correctness 多线程安全问题。比如使用了非线程安全的类,如DateFormats,后面给了例子。

(3)Malicious code vulnerability 恶意代码漏洞 。看着比较吓人的提示,但主要是一些属性直接使用public让别的类来获取,建议改为private并为其提供get/set方法;还有一些public的静态字段,可能会被别的包获取之类的;这些也需要根据项目具体情况来,个人意见,在有的不重要类,有时直接公开使用属性,可能更为便捷.如果你认为这些不需要修改,完全可以忽略。

(4)Dodgy code 糟糕垃圾的代码 。·

比如一个double/float被强制转换成int/long可能会导致精度损失,一些接近零的浮点数会被直接截断,事实上我们应该保留。

在类型转换的时候,我们应该为类型转换提供一个安全的转换方法,因为我们永远不会知道,我们的app在用户手里会发生什么,所以我们要尽可能的去减少这种发生错误的可能。

比如使用switch的时候没有提供default。

比如多余的空检查,就是不可能为空的值,增加了不为空判断,这是没有必要的。属于代码冗余。

比如不安全的类型转换等等。 

……

(5)performance 性能 。主要是一些无用的代码,比如声明了没有用到的属性等等。

(6)correctness 代码的正确性 这一项最重要,没有对变量进行不为空判定,在特殊情况可能发生空指针异常。

 

Attention: 推荐看下《app研发录》《java开发规范》

 

常见错误示例

1)线程安全性问题

As the JavaDoc states, DateFormats are inherently unsafe for multithreaded use. The detector has found a call to an instance of DateFormat that has been obtained via a static field. This looks suspicous.

描述:使用了线程不安全的SimpleDateFormat sdf

解决办法

(1)将上述变量定义为局部变量。缺点:每调用一次方法就会创建一个SimpleDateFormat对象,方法结束又要作为垃圾回收。

(2)使用synchronized给对象关键词加锁,即 synchronized (sdf) 性能较差,每次都要等待锁释放后其他线程才能进入使用该对象。

(3)使用ThreadLocal,为每个线程创建SimpleDateFormat对象副本,可提高性能。这里推荐最后一种解决办法。

public class DateUtil {  
private static ThreadLocal<SimpleDateFormat> local = new ThreadLocal<SimpleDateFormat>(); 
    public static Date parse(String str) throws Exception {  
        SimpleDateFormat sdf = local.get();  
        if (sdf == null) {  
            sdf = new SimpleDateFormat("dd-MMM-yyyy", Locale.US);  
            local.set(sdf);  
        }  
        return sdf.parse(str);  
    }        

    public static String format(Date date) throws Exception {  
        SimpleDateFormat sdf = local.get();  
        if (sdf == null) {  
            sdf = new SimpleDateFormat("dd-MMM-yyyy", Locale.US);  
            local.set(sdf);  
        }  
        return sdf.format(date);  
    }  
}  

2)EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC

equals method overrides equals in superclass and may not be symmetric

描述:equals方法覆盖了父类的equals,可能功能不符实际。

3)May expose internal representation by incorporating reference to mutable object

描述:可能存在值被修改的危险,即Data类型赋值存在引用传递危险

原因:Date类型是引用传递,不是值传递,Data B 赋给Date A之后,当Date B修改之后,Date A也会发生变化,所以存在危险。

解决办法:利用浅克隆,生成副本赋值,this.passTimeStart = (Date)passTimeStart.clone();

4)Class defines compareTo(...) and uses Object.equals()

描述: Java Bean类实现了Comparable接口,但却使用了父类(java.lang.Object类)的equals方法,没有覆盖父类的实现,违反了接口的使用规则。

解决办法:这个Java Bean类中覆盖父类的equals方法。实现equals方法时,需要同步实现hashCode方法。

5)Inefficient use of keySet iterator instead of entrySet iterator

描述:建议使用EntrySet而不是keySet

原因:后者比前者高一倍效率,entrySet方法一次拿到所有key和value的集合;而keySet拿到的只是key的集合,针对每个key,都要去Map中额外查找一次value,从而降低了总体效率;

解决办法:使用如下例子

Iterator<Map.Entry<String,List<DataDictDTO>>> iterator=dictMap.entrySet().iterator();
while(iterator.hasNext()){
  Map.Entry<String,List<DataDictDTO>> e=iterator.next();
  if (CollectionTool.isNotBlank(e.getValue())) {
    map.put(e.getKey(), JacksonTool.bean2Json(e.getValue()));
  }
}

6)ICAST_INTEGER_MULTIPLY_CAST_TO_LONG

Result of integer multiplication cast to long

描述:整形数做乘法运算结果转换为long值时超出范围;

解决办法:附加转为办法

(1)将long型转化为int型,这里的long型是基础类型:

long   a = 10;     int b = (int)a;   

(2)将Long型转换为int 型的,这里的Long型是包装类型:

Long a = 10; int b=a.intValue();

(3)将int型转化为long型,这里的int型是基础类型:

int a = 10;long b = (int)a;

(4)将Integer型转化为long型,这里的Integer型是包装类型:

int a = 10;Long b = a.longValue();

7)A boxed value is unboxed and then immediately reboxed. 

描述:使用三目运算符时,类型不一致,包装类和基础类反复拆箱装箱,装箱的值被拆箱,然后立刻重新装箱。

解决办法:三元运算符两个分支的返回类型保持一致。 

8)Method concatenates strings using + in a loop

The method seems to be building a String using concatenation in a loop. In each iteration, the String is converted to a StringBuffer/StringBuilder, appended to, and converted back to a String. This can lead to a cost quadratic in the number of iterations, as the growing string is recopied in each iteration.

Better performance can be obtained by using a StringBuffer (or StringBuilder in Java 1.5) explicitly.

描述:在循环里面使用了字符串串联使用方法+

原因:该方法似乎是建立在循环使用字符串串联。在每次迭代中,字符串转换为一个StringBuffer / StringBuilder的,附加到字符串,并转换回为String。这可能导致成本的二次迭代,因为不断增长的字符串是在每次迭代中重新复制。

解决办法:为了有更好的性能,可使用StringBuffer或StringBuilder。

 

9)Method may fail to clean up stream or resource on checked exception

描述及原因:可能在关闭之前就出现异常退出了,另外释放资源资源之前需要判空,释放资源时需要扑捉异常

解决办法:正确姿势为

private static String FileWriter() {
        FileWriter fw = null;
        try {
            fw = new FileWriter("summer");
            fw.append("_heart");
        } catch (Exception e) {
            return e.toString();
        } finally {//不管成不成功都关闭
            if (fw != null) {
                try {
                    fw.close();
                } catch (Exception e2) {
                    return e2.toString();
                }
            }
        }
        return null;
}

10)Reliance on default encoding

Found a call to a method which will perform a byte to String (or String to byte) conversion, and will assume that the default platform encoding is suitable. This will cause the application behaviour to vary between platforms. Use an alternative API and specify a charset name or Charset object explicitly.

描述:FileWriter FileReader 是不带编码格式的,默认使用本机器的默认编码,那么就会因为编码问题而出现bug的

解决办法:使用父类实现编码,则可以避免这个问题

public void generateErrorCode(File file)  {
  BufferedWriter writer=null;
  try {
    OutputStreamWriter fileWriter=new OutputStreamWriter(new FileOutputStream(file),"UTF-8");
    writer=new BufferedWriter(fileWriter);
    for (Srting str : strList) {
      writer.write(str);
    }
  }catch (IOException e1) {
    e1.printStackTrace();
  }catch (Exception e2) {
    e2.printStackTrace();
  }finally {
    if(writer!=null){
      try{
        writer.close();
      }catch (Exception e3){
        e3.printStackTrace();
      }
    }
  }
}

11)Exception is caught when Exception is not thrown

描述:异常捕捉有问题

原因:一般出现错误就是这种代码导致的

try{
   doSomething(……)
}catch(Exception ex){
  //
}

解决办法:JAVA规范中并不推荐这样做,这样是属于“过泛地捕获异常”,因为try{}中可能出现的异常种类有很多,上面的做法不利于分别处理各种异常,建议根据业务需求,分别捕获需要特别处理的异常,例子如下:

try{
   doSomething(……)
}
catch(SQLException ex){

}catch(IOException ex){

}catch(Exception ex){

}

 

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值