当前位置: 首页 > 工具软件 > FindBugs > 使用案例 >

findbugs 错误分析

郜卓君
2023-12-01
findbugs 错误分析日志 findbugs 出错类型及对应解释
终于 我们又开始使用FINDBUGS来检测代码的问题了 但因没又中文版和网上错误分析实际很少 所以自己边处理BUG边写文档
   首先在装好FINDBUGS后 在 project-->properteis-->findbugs里的2个框的勾点上可以让改正BUG后自动判断是否改正了 改正了就自动去掉BUG点
   
1、Dead store to local variable 本地变量存储了闲置不用的对象
举例:
List accountCoList = new ArrayList();
我们为accountCoList新建了一个对象,但是程序的后面并没有使用这个这个新建对象。
建议改为:
List accountCoList = null;
 
This instruction assigns a value to a local variable, but the value is not read or used in any subsequent 
instruction. Often, this indicates an error, because the value computed is never used.
Note that Sun's javac compiler often generates dead stores for final local variables. Because FindBugs is 
a bytecode-based tool, there is no easy way to eliminate these false positives.
本地变量存储了闲置不用的对象,也就是说这个变量是多余的。
Hashtable   hTable = new  Hashtable();   
Object   obj = new   Object();  
obj =  hTable.put("uuid",   "abcd1234");  
 String abc = "abc";
 String xyz = new String("");
 xyz = abc;
 System.out.println(xyz); 
用findbug檢查會出現Dead store to local variable的錯誤,他的意思是“本地变量存储了闲置不用的对象”
為什么會出現這個原因呢? 因為 String xyz = new String("");
  这一句执行3个动作:   
  1)创建一个引用xyz   
  2)创建一个String对象   
  3)把String的引用赋值给xyz
  其中,后面两个动作是多余的,因为后面的程序中你没有使用这个新建的String对象,而是重新给xyz赋值,  
xyz = abc;所以,只需要String xyz = abc; 就可以了。这样,findbugs就不会报了。


2、Write to static field from instance method 向static字段中写入值
举例:
 private static DBRBO dbrBO;
 public final void refresh() {
        danskeBankBO = null;
        dbrBO = null;
        fileAndPathBO = null;
    }
建议改为:
去掉static。
 
This instance method writes to a static field. This is tricky to get correct if multiple instances are 
being manipulated, and generally bad practice.
向static字段中写入值,如:


private static Logger logger;
 public XXXActionCtrl(){
  logger = Logger.getLogger(getClass());
 }
可改为:private static Logger logger = Logger.getLogger(getClass());Unread field: should this field be static?
This class contains an instance final field that is initialized to a compile-time static value. Consider 


3、Load of known null value 大体意思是加载了null的对象。
举例
        if (null == boList) {
            for (int i = 0; i < boList.size(); i++) {
                entityList.add(productBOToEntity(boList.get(i)));
            }
        }

4、Exception is caught when Exception is not thrown 
这个意思比较好理解:就是catch了异常但是try里并没有抛出异常
    异常被捕获但没抛出。。。。
     一般人都会这样写代码:
  try{
    //
  }
  catch(Exception ex){
    //
  }
    这样很省事,但是JAVA规范中并不推荐这样做,这样是属于“过泛地捕获异常”,因为try{}中可能出现的异常种类有很多,上面的做法不利于分别处理各种异常,
建议根     据业务需求,分别捕获需要特别处理的异常,例子如下:
  try{
    //
  }
  catch(SQLException ex){
    //
  }
  catch(IOException ex){
    //
  }
  catch(Exception ex){
    //
  }
    另外一个是,捕获到的Exception应尽量记录到LOG文件里。 

5、Method ignores exceptional return value 
没有对方法的异常返回值进行检查


6、Comparison of String objects using == or !=
This code compares java.lang.String objects for reference equality using the == or != operators.
Unless both strings are either constants in a source file, or have been interned using the String.intern() method,
 the same string value may be represented by two different String objects. Consider using the equals(Object) method
  instead.
  从字面意思可以理解String对象进行比较的时候:只有两种情况可以使用== or !=的,这两种情况是;在源文件中是个常数或者是调用
  String.intern()方法,使用String的规范化表示形式来进行比较,如果不是这两中情况的话推荐使用.equals(object)方式
  
7、Method names should start with a lower case letter 
这个好理解方法名的第一个字母不能是大写 函数的首字母应该小写。


8、Non-transient non-serializable instance field in serializable class
This Serializable class defines a non-primitive instance field which is neither transient, Serializable,
 or java.lang.Object, and does not appear to implement the Externalizable interface or the readObject()
  and writeObject() methods.? Objects of this class will not be deserialized correctly if a non-Serializable object
   is stored in this field.
这个错误的意思是:在可序列化的类中存在不能序列化或者不能暂存的数据
在可序列化的类中存在不能序列化或者不能暂存的数据


9.Call to equals() comparing different types
    大部分都是类型永远不会有这种情况 比如a为DOUBLE类型所以EQUALS只匹配字符串 if(a.equals())或if(a.quals())这类判断是根本不会有用的的
equals比较了不同的对象类型 说的是equals要比较相同的对象类型
This method calls equals(Object) on two references of different class types with no common subclasses. 
Therefore, the objects being compared are unlikely to be members of the same class at runtime (unless some 
application classes were not analyzed, or dynamic class loading can occur at runtime). According to the 
contract of equals(), objects of different classes should always compare as unequal; therefore, according 
to the contract defined by java.lang.Object.equals(Object), the result of this comparison will always be 
false at runtime.
调用equals()比较不同的类型。
此方法调用相当于两个不同的类类型的引用,没有共同的子类(对象)。
因此,所比较的对象是不太可能在运行时相同的类成员(除非一些
应用类没有分析或动态类加载可以发生在运行时)。据
equals()的规则,不同类的对象应始终比较不平等,因此,根据
由java.lang.Object.equals定义的合同(对象),FALSE将永远是比较的结果
在运行时错误。
 
10.Class doesn't override equals in superclass
  super.equals(obj) 调用父类equals方法 一般都是Object的方法,所以这个super可写可不写,一般都是 为了代码的可读性才加上去的
    一般就是重写equals(obj)即可 即public boolean equals(Object obj){ return super.equals(obj);}
    但是如果覆盖了equals()方法的话,则必须要覆盖hashCode()方法。否则FINDBUGS会出现下面的7号BUG:覆盖了equals()方法的话,则必须要覆盖hashCode()方法
    所以 public boolean equals(Object obj){ return super.equals(obj);} 
         public int hashCode(){
   return super.hashCode();
}


11.Class is Serializable, but doesn't define serialVersionUID
    serialVersionUID 用来表明类的不同版本间的兼容性
    简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,
JVM会把传来的字节流中的serialVersionUID与本地      相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,
可以进行反序列化,否则就会出现序列化版本不一致的异常。
    当实现java.io.Serializable接口的实体(类)没有显式地定义一个名为serialVersionUID,类型为long的变量时,
Java序列化机制会根据编译的class自动生成一个       serialVersionUID作序列化版本比较用,这种情况下,
只有同一次编译生成的class才会生成相同的serialVersionUID 。
    如果我们不希望通过编译来强制划分软件版本,即实现序列化接口的实体能够兼容先前版本,未作更改的类,
就需要显式地定义一个名为serialVersionUID,类型为long     的变量,不修改这个变量值的序列化实体都可以相互进行串行化和反串行化。
    也就是这个错误 你要定义一个名为 serialVersionUID,类型为long的变量 按照新版Eclipse自动填写规则 就是:
     private static final long serialVersionUID = 1L;
 
12.Class names shouldn't shadow simple name of superclass
   基本就是这个类的名字跟超类的名字一样但不在一个包里 所以就改下类名啦
   
13.Comparison of String parameter using == or !=
   原因:当比较两个字符串内容是否相同时,仅当两个字符串在源文件中都是常量时或者是使用intern()来比较才可以用==来比较,
   否则最好使用对象比较方法equal。附string比较:
    String str1 = "java";
    String str2 = "java";
    System.out.print(str1==str2);
    结果:true(二者都为常量)
    String str1 = new String("java");
    String str2 = new String("java");
    System.out.print(str1==str2);
    结果:false(二者为对象)
    String str1 = "java";
    String str2 = "blog";
    String s = str1+str2;
    System.out.print(s=="javablog");
    结果:false(s不为常量,为对象)
    String s1 = "java";
    String s2 = new String("java");
    System.out.print(s1.intern()==s2.intern());
    结果:true(但是intern()方法在效率和实现方式上不统一)


14.Equals checks for noncompatible operand
    equals()方法比较的是值是否相同,而不是内存指向地址
   就实际情况来看 是因为
   public boolean equals(Object object) {
   if (!(object instanceof DmZzmm)) {
    return false;
   }
         Dxinfo rhs = (Dxinfo) object;
    return new EqualsBuilder().append(this.dxcount, rhs.dxcount).append(this.lxrsjh, rhs.lxrsjh)
      .append(this.dxnr, rhs.dxnr).append(this.fssj, rhs.fssj).append(this.fssl, rhs.fssl)
      .append(this.id,rhs.id).isEquals();
            。。。。
      } 
      问题在那里?很简单 这个白痴是拷贝的代码 既然object 不为DmZzmm就为false 
 而你要执行的是 Dxinfo rhs = (Dxinfo) object; 所以如果为DMZzmm进入代码
 会因为不是 Dxinfo 类型而不执行下面的代码 如果为Dxinfo 类型 它就直接执行为FALSE!!所以代码毫无意义 只会执行false!!
      所以只需要把
      public boolean equals(Object object) {
   if (object instanceof Dxinfo) {
    Dxinfo rhs = (Dxinfo) object;
    。。。。。。。
   }else{
    return false;
   }
 就可以了 说穿了 就是你准备用instanceof 匹配的类要跟你下面执行的类要一致。。。。

15.equals method always returns false
   equals始终返回false
      嘛。。。就是没继承超类的话要自己写个EQUALS。。。。别写的没意义只有false就是了。。
      public boolean equals(Object o) {
   return (this==o);
}


16.equals() method does not check for null argument
   equals()方法没检查空参数
      2B错误完全不解释
 
17.Field names should start with a lower case letter
    字段名应该用小写

18.Can't close pw since it is always null
    无法关闭【PW】因为总是为NULL
 
19.AM: Creates an empty jar file entry (AM_CREATES_EMPTY_JAR_FILE_ENTRY)/AM:
 Creates an empty zip file entry (AM_CREATES_EMPTY_ZIP_FILE_ENTRY)
示例代码: 
ZipEntry entry = new ZipEntry(PATH);
zos.putNextEntry(entry);
zos.closeEntry();
原因:
代码中在调用putNextEntry()之后紧接着调用了closeEntry()函数,致使该jar文件内容为空,
因为打jar包的写内容是在putNextEntry()和closeEntry()两个函数调用之间来进行的。(有时候也许会有意的构建一个空目录,因此不一定就是bug)


20.BC: Equals method should not assume anything about the type of its argument (BC_EQUALS_METHOD_SHOULD_WORK_FOR_ALL_OBJECTS)
示例代码:
public class Foo {
   // some code
   public void equals(Object o) {
     Foo other = (Foo) o;
    // the real equals code
  }
}
原因:
当你在实现类的equals方法时,不应该对参数有任何的预先设定。如上代码所写,
则设定了参数o肯定是Foo类的一个对象.但是如果在函数调用时,参数o不是一个Foo类或其子类,
就会导致代码会抛出一个ClassCastException。因此在实现equals方法,应该加一个判断,如果参数o不是一个Foo类对象,则返回false。


21.BC: Random object created and used only once (DMI_RANDOM_USED_ONLY_ONCE)
示例代码:
public int getRandom(int seed) {
    return new Random(seed).nextInt();
}
原因:
由于java.util.Random是一个伪随机函数,如果传入的seed值相同的话,返回的随机数者是相同的 。
因此没必要每次都new一个新的random出来计算随机数。如果你想真正地获得一个不可预知的随机数,
建议使用java.security.SecureRandom,该类继承自Random,是一个强随机数生成器 。因此上述代码可以修改为:
public class Test  extends Thread{
    private SecureRandom ran;
    Test(int seed){
        ran = new SecureRandom();
    }


    public int getRandom(int seed) {
        return ran.nextInt();
    }
}


22.CN: Class implements Cloneable but does not define or use clone method (CN_IDIOM)
示例代码:
public class Foo implements Cloneable {
         public Object clone() throws CloneNotSupportedException {
                  return super.clone();
         }


}
原因:
类定义要实现了 Cloneable接口,却没有定义或使用 clone方法,即缺少红色字体部分。
 
23.CN: clone method does not call super.clone() (CN_IDIOM_NO_SUPER_CALL)
示例代码:
public class Foo implements Cloneable {
         public Object clone() throws CloneNotSupportedException {
                   return super.clone();
         }
}
原因:
clone方法没有调用super.clone()方法,如果没有调用,则会导致对象父子层级关系不能正确建立,最终导致无法正确组装对象。


24.CN: Class defines clone() but doesn't implement Cloneable (CN_IMPLEMENTS_CLONE_BUT_NOT_CLONEABLE)
示例代码:
public class Foo{
         public Object clone() throws CloneNotSupportedException {
                   return super.clone();
         }
}
原因:
这个用法的意义在于你可以规范该类的子类的clone的实现,如果你的确想这样做的话,这不是一个bug,否则的话是一个bug


25.DE: Method might drop exception (DE_MIGHT_DROP)/DE: Method might ignore exception (DE_MIGHT_IGNORE)
示例代码:
try{}catch(Exception ex){}
原因:
方法有可能抛异常或者忽略异常,需要对异常进行处理,即需要在catch体中对异常进行处理。


8.DMI: Don't use removeAll to clear a collection (DMI_USING_REMOVEALL_TO_CLEAR_COLLECTION)
原因:
建议不要使用 collection.removeAll(collection)方法来删除 collection中的所有元素,而使用collection.clear()。比较二者的代码实现就可以看出:
removeAll()源码:
    public boolean removeAll(Collection<?> c) {
              boolean modified = false;
              Iterator<?> e = iterator();
              while (e.hasNext()) {
                    if (c.contains(e.next())) {
                                e.remove();
                                 modified = true;
                  }
              }
              return modified;
    }
clear()源码:
    public void clear() {
              Iterator<E> e = iterator();
              while (e.hasNext()) {
                  e.next();
                  e.remove();
              }
    }
前者是比较参数中的collection和要移除元素的collection中是否有交集,然后将交集元素删除;
后者是直接将collenction中的元素删除。显然后者要比前者高效,而且对于某些特殊的collenction还容易抛出一些异常,
如ConcurrentModificationException


26.May expose internal representation by incorporating reference to mutable object
    JAVA里,对象是引用传递的,setObj的时候,对象不要直接赋值(this.regDate = regDate),可改为:this.regDate = (Date)regDate.clone();,
 
27.Uninitialized read of field in constructor
      构造函数没有初始化;


28.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的)会更好一些。
For example:
  // This is bad
  String s = "";
  for (int i = 0; i < field.length; ++i) {
    s = s + field[i];
  }
  // This is better
  StringBuffer buf = new StringBuffer();
  for (int i = 0; i < field.length; ++i) {
    buf.append(field[i]);
  }
  String s = buf.toString();


29.
May expose internal representation by incorporating reference to mutable   
object.This code stores a reference to an externally mutable   
object into the internal representation of the object.If instances  
are accessed by untrusted code,and unchecked changes to the mutable object would compromise security or 
other important properties,you will need to do something different.  
Storing a copy of the object is better approach in many situations.
可能因使引用可指向多个对象而暴露内部存储结构。
这代码使一个指向外部多个对象的引用指向了一个内部对象存储地址。  
如果实例被未被信任代码访问或多个对象发生了未经检查的改变就会危及安全性或其它重要属性,
你需要去做一些不同的事情。存储一个对象的拷贝在许多情况下会是一个更好的方法。
网上findbug使用的介绍文章中写到,按下面修改findbug就没bug提示了,
为什么要放到一个临时变量中就可以了?
public class Test {
private String[] name;
public String[] getName() {
String[] temp = name;
return temp;
}public void setName(String[] name) {
String[] temp = name;
this.name = temp;
}
}
因为代码中会经常出现getter/setter,我觉得这个bug是不必要进行修改的。


30.
making the field static.
未读的领域:这个领域应该是静态的?
这个类包含一个实例的最后字段初始化为编译时静态值。考虑静态的领域。(findbugs建议修改该属性为static的)。
如:private final String FAIL_FLAG = "exit";
改为:private static final String FAIL_FLAG = "exit";


31.
Unread field
This field is never read.  Consider removing it from the class. 
未读的领域(字段,属性)
类中声明了从未用过的字段。考虑从类中删除。


32.
Inefficient use of keySet iterator instead of entrySet iterator
This method accesses the value of a Map entry, using a key that was retrieved from a keySet iterator. 
It is more efficient to use an iterator on the entrySet of the map, to avoid the Map.get(key) lookup.
低效利用,使用keySet迭代器而不是entrySet迭代器。
使用entrySet效率会比keySet高。
keySet()迭代后只能通过get()取key。
entrySet()迭代后可以e.getKey(),e.getValue()取key和value,返回的是Entry接口。


33.
Field isn't final but should be.
A mutable static field could be changed by malicious code or by accident from another package. The field 
could be made final to avoid this vulnerability.
字段应该声明为final,实际上却未声明final。
一个易变的static字段可以被恶意代码改变,使用final关键字以避免此漏洞。


34.
Call to equals() with null argument.
This method calls equals(Object), passing a null value as the argument. 
According to the contract of the equals() method, this call should always return false.
使用null参数调用equals()。
此方法调用等于(对象),作为参数传递一个空值。根据合同的equals()方法,此调用应始终返回false。
如:queryStr.equals(null); 这样使用是不可取的,虽然能够通过编译。


35.
Invocation of toString on an array.
The code invokes toString on an array, which will generate a fairly useless result such as [C@16f0472. 
Consider using Arrays.toString to convert the array into a readable String that gives the contents of the array. 
See Programming Puzzlers, chapter 3, puzzle 12.
对数组调用toString()方法。
代码在对数组调用toString()方法时,将产生一个相当无用的形如 [C@16f0472 的结果。考虑使用 Arrays.toString方法


将数组转化为一个可读的给出数组内容的字符串。
比如:在使用System.out.println(xx.readNext());时候会碰到这样的提示,readNext() 方法放回一个String[]。
可改为:
String[] arr = reader.readNext();
System.out.println(Arrays.asList(arr).toString());
(好像有点麻烦,没想到更简洁的办法)。


36.
Nullcheck of value previously dereferenced
A value is checked here to see whether it is null, but this value can't be null because it was previously 
dereferenced and if it were null a null pointer exception would have occurred at the earlier dereference. 
Essentially, this code and the previous dereference disagree as to whether this value is allowed to be 
null. Either the check is redundant or the previous dereference is erroneous.
此代码之前废弃null值检查。
一个值被选中这里看它是否是空的,但这个值不能为空,因为它在此之前废弃null值检查,而且如果它为null,空指针异
常将会发生在此处,如果它是空一空指针异常会发生在较早取消引用。
从本质上讲,此代码和前边废弃的null值检查将会出现不一致,是否这个值是容许
空。
出现该bug有两种情况:多余的null检查;前边废弃null值检查的。
比如:我们经常会这个使用ActionForm,
String clazzId = request.getParameter("clazzId");// script1
studentForm.setClazzID(clazzId);// script2
往往会在script2会出现该错误,因为在script1出未检查clazzId是否为null才导致的。
修改为 : 
if(clazzId != null) {
studentForm.setClazzID(clazzId);
}
在设置使用clazzId之前先判断其是否为null。


37.
Possible null pointer dereference in method on exception path
A reference value which is null on some exception control path is dereferenced here.  This may lead to a NullPointerException when the code is executed.  Note that because FindBugs currently does not prune infeasible exception paths, this may be a false warning.
Also note that FindBugs considers the default case of a switch statement to be an exception path, since the default case is often infeasible.
在异常部分放弃null值检查,可能会导致后面的代码出现空指针异常。如:
md = null;
try {
   md = MessageDigest.getInstance("SHA-256");
   md.update(bt);
  } catch (NoSuchAlgorithmException e) {
   e.printStackTrace();// script1
  }
  byte[] digest = md.digest();// script2
bug出现在script2处,在script1处处理相应的exception即可,如throw 或 return; 


Possible null pointer dereference
There is a branch of statement that, if executed, guarantees that a null value will be dereferenced, which would generate a NullPointerException when the code is executed. Of course, the problem might be that the branch or statement is infeasible and that the null pointer exception can't ever be executed; deciding that is beyond the ability of FindBugs.
可能的空指针引用。
如在JDBC编程时候,在关闭ResultSet时候(rs.close()),经常会出现这个bug,解决办法很容易想到,判断是否为null或


使用try...catch...finally。


---------------------------------------------------------------------
    
相关资料:
 
1:hyddd的FindBugs分析记录
用FindBugs分析代码漏洞 hyddd  阅读:6139 评论:13  
http://www.cnblogs.com/hyddd/tag/hyddd%E7%9A%84FindBugs%E5%88%86%E6%9E%90%E8%AE%B0%E5%BD%95/ 


1.[hyddd的FindBugs分析记录][H STCAL] Call to static DateFormat 
上面的英文解释其实应该说得比较清楚,在Java文档中,已经明确说明了DateFormats 是非线程安全的,
而在SimpleDateFormat的Jdk 的Source文件中,我们也找到这么一段注释,说明它不是线程安全的。  
导致SimpleDateFormat出现多线程安全问题的原因,是因为:SimpleDateFormat处理复杂,Jdk的实现中使用了成员变量来传递参数,这就造成在多线程的时候会出现错误。 
而Findbugs所说的“Call to static DateFormat”,其实就是一些人:
为了渐少new 的次数而把SimpleDateFormat做成成员或者静态成员,上面已经说了,这样做是不安全的。


2.[hyddd的FindBugs分析记录][M S XSS] Class defines clone() but doesn't implement Cloneable 
[H B CN] Class defines clone() but doesn't implement Cloneable [CN_IMPLEMENTS_CLONE_BUT_NOT_CLONEABLE] 
This class defines a clone() method but the class doesn't implement Cloneable. 
There are some situations in which this is OK (e.g., you want to control how subclasses can clone themselves), 
but just make sure that this is what you intended. 
  什么代码会引起这个问题呢?先看下面: 
  1: class MyTest {
  2: public MyTest clone(){
  3: MyTest test = new MyTest();
  4: return test;
  5: }
  6: }
这段代码会引起FindBugs的这个警告,为什么?请看下面解释:
1.根据FindBugs的说明,如果一个类重写clone()函数,而不继承Cloneable接口,是一定有问题的,如果clone()方法只是简单进行克隆,
如:new一个对象并初始化,然后返回这个新创建的对象的话,不继承Cloneable接口也是可以的。
2.如果是上面这样的话,为什么还要继承Cloneable接口呢?稍微说一下,Cloneable接口是不包含任何方法的!
其实这个接口仅仅是一个标志,而且这个标志也仅仅是针对Object类中clone()方法的,如果clone类没有实现 Cloneable接口,
并调用了Object的clone()方法(也就是调用了super.Clone()方法),那么Object的clone() 方法就会抛出CloneNotSupportedException异常。
3.所以这里建议是:规范写法,如果重写clone(),最好请继承Cloneable接口。


3.[hyddd的FindBugs分析记录][M S XSS] Servlet reflected cross site scripting vulnerability 
[M S XSS] Servlet reflected cross site scripting vulnerability [XSS_REQUEST_PARAMETER_TO_SERVLET_WRITER]
This code directly writes an HTTP parameter to Servlet output, which allows for a reflected cross site scripting vulnerability. 
See http://en.wikipedia.org/wiki/Cross-site_scripting for more information.
FindBugs looks only for the most blatant, obvious cases of cross site scripting. If FindBugs found any, 
you almost certainly have more cross site scripting vulnerabilities that FindBugs doesn't report. If you are concerned about cross site scripting, you should seriously consider using a commercial static analysis or pen-testing tool.
先看下面代码:
public void doGet(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{
  //
  String v = request.getParameter("v");
  //
  PrintWriter out = response.getWriter();
  out.print("协议版本号不对,v="+v);
  out.close();
  //
}
这里字符串v没有作过滤,直接返回给用户,有可能操作XSS攻击。具体关于XSS攻击的资料,可以参考上面Findbugs说明中的连接,这里就不多说了。


4.[hyddd的FindBugs分析记录]M D ICAST] Result of integer multiplication cast to long 
[M D ICAST] Result of integer multiplication cast to long [ICAST_INTEGER_MULTIPLY_CAST_TO_LONG]
This code performs integer multiply and then converts the result to a long, as in: 
  long convertDaysToMilliseconds(int days) { return 1000*3600*24*days; } 
If the multiplication is done using long arithmetic, you can avoid the possibility that the result will overflow. 
For example, you could fix the above code to: 
long convertDaysToMilliseconds(int days) { return 1000L*3600*24*days; } 
or 
static final long MILLISECONDS_PER_DAY = 24L*3600*1000; long convertDaysToMilliseconds(int days) { return days * MILLISECONDS_PER_DAY; } 
其实看上面的例子可以看到了
long convertDaysToMilliseconds(int days) { return 1000*3600*24*days; }  
这段代码是有可能溢出的,建议把代码改为下面:
long convertDaysToMilliseconds(int days) { return 1000L*3600*24*days; } 
用过VB6的人应该了解为什么会这样,因为在VB6里面一不小心就有可能出现这种溢出,在这里,JAVA认为:int * int *
 .....它的结果还是一个int!为什么编译器不能自动识别呢?答案是:很麻烦,并且这会使用编译器的效率非常低!
 对于有汇编经验的人来说,应该了解用汇编实现动态判断一个结果应该分配到一个long的空间还是int的空间有多复杂吧,
 而编译器有不能随便随便把int * int 的结果分配一个long空间,因为这会非常浪费内存,所以出于对效率和资源的考虑,
 最后的决定就是,凡是int * int * ....这样的计算结果一律都存放到int空间里,如果是long * int * .....则一律存放到long空间里,
 这就解释了为什么上面要在1000后面加个"L"了。:>


[hyddd的FindBugs分析记录][M B DE] Method might ignore exception  
This method might ignore an exception.  In general, exceptions should be handled or reported in some way, 
or they should be thrown out of the method.
try{
//
}
Catch(Execption ex){}
//
上面这段代码没有对ex进行任何处理。Findbugs在这里想说明的就是上面这个问题。


5.[hyddd的FindBugs分析记录][M M IS] Inconsistent synchronization追加说明 


6.[hyddd的FindBugs分析记录][M B Eq] Class defines compareTo(...) and uses Object.equals() 
重写compareTo有一定的风险,因为你不知道JDK内部做对象对比时,到底使用了compareTo还是equals。
例如:在JAVA5 里,PriorityQueue.remove中使用了compareTo,但JAVA6中,PriorityQueue.remove使用了equals方法。
  这里Findbugs强烈建议:(x.compareTo(y)==0) == (x.equals(y)),当你重写compareTo的时候,请记得这一点:>,当然这个只是建议,不是绝对的。


7.[hyddd的FindBugs分析记录][H C EC] equals() used to compare array and nonarray 
This method invokes the .equals(Object o) to compare an array and a reference that doesn't seem to be an array. 
If things being compared are of different types, they are guaranteed to be unequal and the comparison is almost certainly an error. 
Even if they are both arrays, the equals method on arrays only determines of the two arrays are the same object. 
To compare the contents of the arrays, use java.util.Arrays.equals(Object[], Object[]). 
先看下面一段代码:
String[] strs = {"1"};
//.
If("1".equals(strs)){
//todo
}
上面这段代码,if里面的代码是永远都不会被执行的,因为我们用了一个非数据(nonarray)和一个数组(array)做equals(),这里估计是写代码时的笔误。


8.[hyddd的FindBugs分析记录][H C FS] Format string references missing argument 
Not enough arguments are passed to satisfy a placeholder in the format string. 
A runtime exception will occur when this statement is executed.
看实例代码:
public static void main(String args[]) throws Exception{
    String sqlrightDate = "select state from right where user_id = '%s' and soft_code ='%s' and right_name = '%s' ";
    String str = String.format(sqlrightDate,"1" ,"2");
    System.out.println(str);
}
对,你没有眼花,format里面只传了两个参数,但sqlrightData需要的是三个参数!这可能值是你的一个笔误,
相信没人会故意写这样的代码,但编译器检查不出这种问题,这导致运行时这里会抛异常,所以用FindBugs扫扫代码还是有点用处的~!


9.[hyddd的FindBugs分析记录][M B Nm] Class names should start with an upper case letter 
JAVA里,类的首字母需要大写,这个不多说了。


10.[hyddd的FindBugs分析记录][H B BC] Random object created and used only once 
1.new一个Random的对象,保存之,然后每次都使用这个对象去获取随机数,而不要每次new一个Random对象去获取。
2.FindBugs强烈推荐使用java.security.SecureRandom代替Random。


11.[hyddd的FindBugs分析记录][M D REC] Exception is caught when Exception is not thrown 
略 上有


12.[hyddd的FindBugs分析记录][M D DLS] Dead store to local variable  
略 上有


13.[hyddd的FindBugs分析记录][M P UuF] Unused field 
说明某个类里的某个变量没有被使用。FindBugs建议你把无用东西去除掉。


14.[hyddd的FindBugs分析记录][M B ODR] Method may fail to close database resource 
请参考:[M X OBL] Method may fail to clean up stream or resource
注意:同一个问题可能引发两个不同的BUG警告~!


15.[hyddd的FindBugs分析记录][M X OBL] Method may fail to clean up stream or resource  
[M X OBL] Method may fail to clean up stream or resource [OBL_UNSATISFIED_OBLIGATION]
This method may fail to clean up (close, dispose of) a stream, database object, or other resource requiring an explicit cleanup operation.
In general, if a method opens a stream or other resource, the method should use a try/finally block to ensure that the stream
 or resource is cleaned up before the method returns. 
This bug pattern is essentially the same as the OS_OPEN_STREAM and ODR_OPEN_DATABASE_RESOURCE bug patterns, 
but is based on a different (and hopefully better) static analysis technique. We are interested is getting feedback about the 
usefulness of this bug pattern. To send feedback, either: 
send email to findbugs@cs.umd.edu 
file a bug report: http://findbugs.sourceforge.net/reportingBugs.html 
In particular, the false-positive suppression heuristics for this bug pattern have not been extensively tuned, 
so reports about false positives are helpful to us. 
See Weimer and Necula, Finding and Preventing Run-Time Error Handling Mistakes, for a description of the analysis technique.
这个BUG想说明的是:有些资源打开了,但在函数结束的时候却没有关闭。比如:数据库连接......
虽然JAVA有垃圾回收机制,但是,自己打开的资源需要自己手动关闭,否则有可能直到程序退出,JRE才会清理你打开的资源。
这里FindBugs建议在try/finally里面关闭打开的资源,在关闭之前,还要判断资源是否为Null,或者再加一层异常捕获处理噢~
以下是一些可能关闭资源失败的例子
-----------------------------------------------情况1-----------------------------------------
//
FileOutputStream fs = null;
try{
  fs = new FileOutputStream(clearTableFile);
  fs.close();    //当出异常时候fs关闭失败,所以应该在finally中关闭
}
catch(){
//
}
-----------------------------------------------情况2-----------------------------------------
//
Properties props = new Properties();
try{
  props.store(new FileOutputStream(configFile), configFile);  //new FileOutputStream(configFile)没有释放。
}
catch(){
  //
}
-----------------------------------------------情况3-----------------------------------------
  //
  FileOutputStream fs = new FileOutputStream(clearTableFile);
  //    (里没有做异常处理,如果中间出异常了,异常会抛往上层,这时fs就没能释放了。
  fs.close();    
  //


16.[hyddd的FindBugs分析记录][M M NP] Synchronize and null check on the same field   
 [M M NP] Synchronize and null check on the same field. [NP_SYNC_AND_NULL_CHECK_FIELD]
Since the field is synchronized on, it seems not likely to be null. If it is null and then synchronized on a 
NullPointerException will be thrown and the check would be pointless. Better to synchronize on another field.
先看一段代码:
public static Timestamp getTableLastUpdateTime(String tableName) {
    Timestamp time = null;
    synchronized (GoodsSysConfig.tableLastUpdateTime) {
       if (GoodsSysConfig.tableLastUpdateTime != null) {  //这句判断是多余的,可以去掉!
       time = GoodsSysConfig.tableLastUpdateTime.get(tableName);
      } 
      else {
          log.error("GoodsSysConfig.tableLastUpdateTime 为空!");
        }
    }
    return time;
}


这段代码会引发两个BUG警告,一个是:[H C RCN] Nullcheck of value previously dereferenced,
另外一个就是这里要介绍的[M M NP] Synchronize and null check on the same field。其实,
在[H C RCN] Nullcheck of value previously dereferenced的介绍里面已经提到Synchronize and null check on the same field这个问题了,
出现这个BUG的原因是:synchronized (GoodsSysConfig.tableLastUpdateTime) 这里已经对GoodsSysConfig.tableLastUpdateTime这个变量进行了是否为Null的判断,
如果为Null,synchronized (...)会抛异常,并且经过synchronized (...)后,GoodsSysConfig.tableLastUpdateTime已经被独占,
所以 if (GoodsSysConfig.tableLastUpdateTime != null) 这句可以看作是多余的,因为GoodsSysConfig.tableLastUpdateTime不可能为Null。


17.[hyddd的FindBugs分析记录][M V MS] Public static method may expose internal representation by returning array  
[M V MS] Public static method may expose internal representation by returning array [MS_EXPOSE_REP]
A public static method returns a reference to an array that is part of the static state of the class. 
Any code that calls this method can freely modify the underlying array. One fix is to return a copy of the array.
一个静态的公共函数,它返回了一个私有的静态数组的引用。任何调用这个静态公共函数的代码,都有可能改变这个私有的静态数组。实例代码如下:
public static void main(String args[]) throws Exception{
        String[] strs = Test.getStrs();
        strs[0] = "123";
        Test.myTest();
}
public class Test {
    private static String[] strs = new String[10];
    
    public static String[] getStr(){
        return strs;
    }
    public static void myTest(){
        System.out.println(strs[0]);
    }
}
运行结果是:123
防止这种问题的方法是:返回一个数组的拷贝,而不直接返回数组引用。如:
public static String[] getStr(){
        return strs.clone();
}
注意:这个BUG和下面这两个BUG比较类似,可以比较一下:>
[M V EI2] May expose internal representation by incorporating reference to mutable object
[M V EI] May expose internal representation by returning reference to mutable object


18.[hyddd的FindBugs分析记录][M C NP] Possible null pointer dereference   
[M C NP] Possible null pointer dereference [NP_NULL_ON_SOME_PATH]
There is a branch of statement that, if executed, guarantees that a null value will be dereferenced, 
which would generate a NullPointerException when the code is executed. Of course, 
the problem might be that the branch or statement is infeasible and that the null pointer exception can't ever be executed; 
deciding that is beyond the ability of FindBugs.
看一段代码:
public void test(){
  //
  ResultSet rs = cmd.executeQuery();
  if (rs != null && rs.next()) {
    goodsId = rs.getInt("goods_id");
  }
  rs.close();  //rs可能为null
  //
}
这里不多解释,更正代码的方法很多,可以用try...catch...finally...处理可能出现的异常,也可以在rs.close之前,先判断rs是否为null。




19.[hyddd的FindBugs分析记录][M B Nm] Method names should start with a lower case letter  
在Java里,函数的首字母应该小写。
而.Net和Java不同,它要求函数的首字母必须大写。
这个问题是个规范问题,这里就不提供实例代码了。


20.[hyddd的FindBugs分析记录][M P Dm] Method invokes toString() method on a String 
[M P Dm] Method invokes toString() method on a String [DM_STRING_TOSTRING]
Calling String.toString() is just a redundant operation. Just use the String.
对一个String对象使用了toString()方法,这种操作是多余的,完全可以去掉。
public static void main(String args[]) throws Exception{
        String str = "123";
        System.out.println(str.toString());
}


21.[hyddd的FindBugs分析记录][M P Bx] Method invokes inefficient Number constructor; use static valueOf instead 
 [M P Bx] Method invokes inefficient Number constructor; use static valueOf instead [DM_NUMBER_CTOR]
Using new Integer(int) is guaranteed to always result in a new object whereas Integer.valueOf(int) 
allows caching of values to be done by the compiler, class library, or JVM. 
Using of cached values avoids object allocation and the code will be faster. 
Values between -128 and 127 are guaranteed to have corresponding cached instances and using valueOf is 
approximately 3.5 times faster than using constructor. For values outside the constant range the performance of both styles is the same. 
Unless the class must be compatible with JVMs predating Java 1.5, use either autoboxing or the valueOf() method when creating instances of Long, 
Integer, Short, Character, and Byte.
这里,FindBugs推荐使用Integer.ValueOf(int)代替new Integer(int),因为这样可以提高性能。
如果当你的int值介于-128~127时,Integer.ValueOf(int)的效率比Integer(int)快大约3.5倍。
下面看看JDK的源码,看看到Integer.ValueOf(int)里面做了什么优化:
public static Integer valueOf(int i) {
  final int offset = 128;
  if (i >= -128 && i <= 127) { // must cache
    return IntegerCache.cache[i + offset];
  }
  return new Integer(i);

private static class IntegerCache {
  private IntegerCache(){}
    
  static final Integer cache[] = new Integer[-(-128) + 127 + 1];
  static {
  for(int i = 0; i < cache.length; i++)
     cache = new Integer(i - 128);
  }

从源代码可以知道,ValueOf对-128~127这256个值做了缓存(IntegerCache),如果int值的范围是:-128~127,在ValueOf(int)时,
他会直接返回IntegerCache的缓存给你。
所以你会看到这样的一个现象:
public static void main(String []args) {
     Integer a = 100;
     Integer b = 100;
     System.out.println(a==b);


     Integer c = new Integer(100);
     Integer d = new Integer(100);
     System.out.println(c==d);
}
结果是:
true
false
因为:java在编译的时候 Integer a = 100; 被翻译成-> Integer a = Integer.valueOf(100);,
所以a和b得到都是一个Cache对象,并且是同一个!而c和d是新创建的两个不同的对象,所以c自然不等于d。
再看看这段代码:
public static void main(String args[]) throws Exception{
        Integer a = 100;
        Integer b = a;
        a = a + 1;  //或者a++;
        System.out.println(a==b);
}
结果是:false
因为在对a操作时(a=a+1或者a++),a重新创建了一个对象,而b对应的还是缓存里的100,所以输出的结果为false。


22.[hyddd的FindBugs分析记录][M C NP] Method call passes null for unconditionally dereferenced parameter  
[M C NP] Method call passes null for unconditionally dereferenced parameter [NP_NULL_PARAM_DEREF]
This method call passes a null value to a method which might dereference it unconditionally.
这里FindBugs的解释是:你传入参数的值,有可能是一个NULL。下面看一段代码:
public boolean accept(File dir, String name) {
            String reg = null;
            if (this.type == ChangeConfigUtil.windowsType) {
                reg = "^(\\S+)" + ChangeConfigUtil.windowsTypeTag
                        + "\\.(\\S*)$";
            } else if (this.type == ChangeConfigUtil.linuxType) {
                reg = "^(\\S+)" + ChangeConfigUtil.linuxTypeTag + "\\.(\\S*)$";
            }
            
            Pattern p = Pattern.compile(reg, Pattern.MULTILINE);  //这里reg可能为空
            Matcher m = p.matcher(name);
            if (m.find()) {
                return true;
            } else {
                return false;
            }
}
这里Pattern p = Pattern.compile(reg, Pattern.MULTILINE);可能会出现问题。
如果this.type既不等于windows,也不等于linux,那么reg=null,这会导致Pattern.compile(reg, Pattern.MULTILINE);抛异常。
这里,FindBugs想说明的就是这个问题。


23.[hyddd的FindBugs分析记录][M V EI] May expose internal representation by returning reference to mutable object 
 这个问题的解决方案和[M V EI2] May expose internal representation by incorporating reference to mutable object很类似,
 可以参考:http://www.cnblogs.com/hyddd/articles/1391118.html
 
24.[hyddd的FindBugs分析记录][M V EI2] May expose internal representation by incorporating reference to mutable object  
[M V EI2] May expose internal representation by incorporating reference to mutable object [EI_EXPOSE_REP2]
This code stores a reference to an externally mutable object into the internal representation of the object. 
 If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important properties, 
you will need to do something different. Storing a copy of the object is better approach in many situations.
这个问题和Inconsistent synchronization描述的问题很类似,解决方案也很类似,可以参考看看:http://www.cnblogs.com/hyddd/articles/1391098.html
先看一段代码:
public class Test  extends Thread{
    public static void main(String args[]) throws Exception{
        Test3 obj = new Test3();
        Date now = new Date();
        
        obj.setRegDate(now);    
        now.setYear(4000);  //问题所在!
        
        System.out.println(obj.getRegDate());
    }
}
public class Test3 {
    private Date regDate ;    
    public void setRegDate(Date regDate) {
        this.regDate = regDate;
    }
    public Date getRegDate() {
        return regDate;
    }    
}
这段代码的输出是:Thu Feb 15 21:47:13 CST 5900
如果main里面不加now.setYear(4000);这句代码呢,结果是:Sun Feb 15 21:47:31 CST 2009
从这里我们发现了,修改一个对象,可能会引起其他对象的修改,因为JAVA里,对象是引用传递的......所以这里我的建议是:
setObj的时候,对象不要直接赋值(this.regDate = regDate),而是赋值传入对象的拷贝(this.regDate = (Date)regDate.clone();)。
OK~现在我们把代码this.regDate = regDate替换成this.regDate = (Date)regDate.clone();,
运行一下看看结果,噢,输出是:Sun Feb 15 21:47:31 CST 2009。


25.[hyddd的FindBugs分析记录][M M IS] Inconsistent synchronization  
[M M IS] Inconsistent synchronization [IS2_INCONSISTENT_SYNC]
The fields of this class appear to be accessed inconsistently with respect to synchronization.  
This bug report indicates that the bug pattern detector judged that 
The class contains a mix of locked and unlocked accesses, 
At least one locked access was performed by one of the class's own methods, and 
The number of unsynchronized field accesses (reads and writes) was no more than one third of all accesses,
 with writes being weighed twice as high as reads 
A typical bug matching this bug pattern is forgetting to synchronize one of the methods in a class that is intended to be thread-safe.
You can select the nodes labeled "Unsynchronized access" to show the code locations where the detector believed that a 
field was accessed without synchronization.
Note that there are various sources of inaccuracy in this detector; for example, the detector cannot statically detect all
 situations in which a lock is held.  Also, even when the detector is accurate in distinguishing locked vs. unlocked accesses, 
 the code in question may still be correct.
先上一段代码:
public class Test  extends Thread{
    public static void main(String args[]) throws InterruptedException{
        ObjectClass obj = new ObjectClass(); 
        Thread t2 = new ChangeValue(obj);
        t2.start();
        Thread t1 = new AlwaysRun(obj);
        t1.start();
        sleep(10000);
        t1.stop();
    }
}
class AlwaysRun extends Thread{
    ObjectClass obj;
    public AlwaysRun(ObjectClass obj) {
        // TODO Auto-generated constructor stub
        this.obj = obj;
    }
    public void run() {
        obj.Loop();
    }
}
class ChangeValue extends Thread{
    ObjectClass obj;
    ChangeValue(ObjectClass obj){
        this.obj = obj;
    }
    
    public void run() {
        System.out.println("Thread2");
        ObjClass2 obj2 = obj.getObj();
        try {
            sleep(1500);
        } catch (InterruptedException e) {
            System.out.println("Error!");
        }
               obj2.str = "aaa";
        System.out.println("Thread2 Finish!");
    }
}
public class ObjectClass extends Thread {


    private ObjClass2 obj;
    private Object lockTable = new Object();
    
    public ObjectClass() {
        // TODO Auto-generated constructor stub
        obj = new ObjClass2();
    }
    public void setObj(ObjClass2 obj){
        synchronized (lockTable){
            this.obj = obj;      
        }
    }
    
    public ObjClass2 getObj(){
        synchronized (lockTable){
            return this.obj;      //出问题处!!
        }
    }
    
    public void Loop(){
        synchronized (lockTable){
            while(true){
                System.out.println(obj.str);
            }
        }
    }    
}
public class ObjClass2 {    
    public String str = "ddddddd";    
}
看看运行的结果:
Thread2
ddddddd
ddddddd
ddddddd
ddddddd
ddddddd
ddddddd
ddddddd
Thread2 Finish!
aaa
aaa
aaa
aaa
aaa
aaa
....


如果看明白代码,你应该会知道问题出再哪里了,为什么Loop使用了synchronized (lockTable),但是obj.str还是被修改了?!
因为getObj()这个函数把obj对象返回了给外面,JAVA里面对象的传递是使用引用传递,如果对象传递到外面并且在做修改obj的时候没有加锁操作,
就是引起刚才的问题。所以如果getObj()函数返回的是对象,那么,请返回一个拷贝,而不要直接返回引用。
这里再总结一下值得注意问题:
1.看下面代码:
    public ObjClass2 getObj(){
        synchronized (lockTable){
            return this.obj;
        }
    }
synchronized (lockTable)不能阻止外面的函数修改obj,即:obj=getObj();当赋值完毕后,synchronized (lockTable)无效了,
如果后面需要修改obj的值,那么就得注意了!!!
另外建议的是,不直接返回this.obj,而是返回一个this.obj的拷贝。这样可以根本上避免出现上面的问题!
2.同理,在setObj(...)的时候,如果传入的是个对象,也建议是存储传入对象的拷贝,而不(this.obj=obj)这样直接赋值。
3.注意对竞争的资源都使用synchronized (lockTable),不要像上面的Demo代码那样,一处用了,一处没有!


26.[hyddd的FindBugs分析记录][M D RCN] Redundant nullcheck of value known to be non-null  
参考文档:[hyddd的FindBugs分析记录][M C RCN] Nullcheck of value previously dereferenced


27.[hyddd的FindBugs分析记录][M D RCN] Repeated conditional tests  
[M C RpC] Repeated conditional tests [RpC_REPEATED_CONDITIONAL_TEST]
The code contains a conditional test is performed twice, one right after the other (e.g., x == 0 || x == 0). 
Perhaps the second occurrence is intended to be something else (e.g., x == 0 || y == 0).
这个不解释,一看代码就知道:
    public void test{
    String str = "123";
            if(str!=null){
                if(str!=null){  //重复了
                 System.out.println("123");
                }
            }
    }

28.[hyddd的FindBugs分析记录][M C RCN] Nullcheck of value previously dereferenced  
[M C RCN] Nullcheck of value previously dereferenced [RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE]
A value is checked here to see whether it is null, but this value can't be null because it was previously 
dereferenced and if it were null a null pointer exception would have occurred at the earlier dereference.Essentially, 
this code and the previous dereference disagree as to whether this value is allowed to be null. 
Either the check is redundant or the previous dereference is erroneous.
先看一段代码:
public class MyTest {
    private String str = "123";
    public void setStr(String str){
        this.str = str;
    }
    public String getStr(){
        return this.str;
    }
    public void test(){
        String str2 = "123";
        synchronized (str) {
            if (str != null) {
                str2 ="123";
            } else {
                str2 ="456";
            }            
            System.out.println(str2);
        }
    }
}
这个时候这段代码就会报Nullcheck of value previously dereferenced这个Bug,看Bug定位,
发现问题出现在synchronized (str) 这里,str没有检查是否为NULL?!OK,我现在改用getStr()这个函数代替直接使用str,
即:synchronized (getStr()),重新FindBug......居然没有发现错误-_-,但事实上getStr()并没有检查str是否为Null!!
  现在我换另外一种写法,代码如下:
public class MyTest {
    private String str = "123";
    public void setStr(String str){
        this.str = str;
    }
    public String getStr(){
        return this.str;
    }


    public void test(){
        String str2 = "123";


        if(str != null){        //tag2
            synchronized (str) {
                if (str != null) {    //tag1
                    str2 ="123";
                } else {
                    str2 ="456";
                }           
                System.out.println(str2);
            }
        }
        
    }
}
这次我在tag2处加了一行检查str是否为NULL的代码,看FindBugs结果,
出现了另外一个中等BUG:[M D RCN]Redundant nullcheck of value known to be non-null,跟踪发现是tag1处的代码是多余的,
因为tag2处已经检查了一遍,并且在synchronized (str)后,str被独占,它不可能被修改,也就是说synchronized (str)后,根本不需要检查str是否为空,
tag1处的代码是多余的。如果把tag1处代码去掉,[M D RCN]警告就没有了。
  不知道大家有没有发现,上面的代码还有个问题,看这段代码:
if(str != null){
        synchronized (str) {      
            System.out.println(“123”);
        }
}
如果是多线程运行时,你不能排除它会if(str != null)和synchronized(str)之间进行线程切换,
然后把str至为null!所以上面这样写其实也是有可能出现问题的!只是FindBugs没有找出来。
我觉得最好的写法还是:
    public void test() throws Exception{
        try{
            String str2 = "123";
            synchronized (getStr()) {
                str2 ="456";
                System.out.println(str2);
            }
        }
        catch(Exception ex){
            throw ex;
        }
   //Do other things....
    }
其实synchronized (getStr()) 换成synchronized (str) 也是可以的,FindBugs不会再报Bug了。 
总结一下,我觉得FindBugs之所以会报[H C RCN] Nullcheck of value previously dereferenced,是因为我没有检查str的值是否为Null,
并且没有注意对可能出现的Exception的截获。而简单使用getStr(),不检查str的值,不作异常捕获,也能躲过这个Bug,
我觉得可能是FindBugs的一个BUG:<,因为估计它会认为,getStr()这个函数里面会检查str的值......但这个仅仅是个人认为而已。


29.[hyddd的FindBugs分析记录][H C FS] More arguments are passed that are actually used in the format string  
[H C FS] More arguments are passed that are actually used in the format string [VA_FORMAT_STRING_EXTRA_ARGUMENTS_PASSED]
A format-string method with a variable number of arguments is called, but more arguments are passed than are actually used by the format string. 
This won't cause a runtime exception, but the code may be silently omitting information that was intended to be included in the formatted string.
这个错误很简单,是使用String.format的时候出了问题,format里面的参数没有被全部用上。
看下面一段代码:
public void test(){
    String str1 = "123";
    String str2 = "456";
    String str3 = String.format("{0} {1}" , str1 ,str2);
    System.out.println(str3);
}
输出的结果是:{0} {1}
这个Bug描述就是这种问题,str1和str2根本没有被用上!{0}{1}这种Format格式是.NET上面的用法,java里面应该是%s %s。
这个是一个代码逻辑问题,可能是你写代码时不小心导致的,它在这段代码里不会导致异常,但往往会很可能导致其他地方异常,那时候你可能会百思不得其解。


-============================================================================================================


2:FindBugs Bug Detector Report
http://www.jquantlib.org/sites/jquantlib/findbugs.html


3:FindBugs Bug Descriptions (推荐)
http://findbugs.sourceforge.net/bugDescriptions.html#NP_NULL_PARAM_DEREF


4.FindBugs缺陷类型统计分析:
http://donsun.javaeye.com/blog/697407
 类似资料: