FindBugs简单应用

时间:2020-12-01 08:40:42

FindBugs是一种java代码的静态分析工具,无需开发人员费劲就能找出代码中可能存在的缺陷。FindBugs 不注重样式或者格式,它试图只寻找缺陷或者潜在的性能问题。

第一步,http://sourceforge.net/projects/findbugs/files/findbugs%20eclipse%20plugin/下载zip包,解压到eclipse的plugins目录,重新启动eclipse。

第二步,工程右键中应该出现了FindBugs,点击应用。

FindBugs简单应用

Bug: Call to equals() comparing different types
Pattern id: EC_UNRELATED_TYPES, type: EC, category:
CORRECTNESS

解释:

两个不同类型的对象调用equals方法,如果equals方法没有被重写,那么调用object的==,永远不会相等。

解决方法

应该改为str.toString()

FindBugs简单应用

Bug: Check
for oddness that won't work for negative numbers
Pattern id: IM_BAD_CHECK_FOR_ODD, type: IM, category:
STYLE

解释:

如果row是负奇数,那么row % 2 == -1,

解决方法:

考虑使用x & 1 == 1或者x % 2 != 0

FindBugs简单应用

Bug:
PerfmSingleGraphPanel$RSCategory defines equals and uses Object.hashCode()
Pattern id: HE_EQUALS_USE_HASHCODE, type: HE, category:
BAD_PRACTICE

解释:

重载了equals方法,却没有重载hashCode方法,如果使用object自己的hashCode,我们可以从JDK源代码可以看到object的hashCode方法是native的,它的值由虚拟机分配(某种情况下代表了在虚拟机中的地址或者唯一标识),每个对象都不一样。

  equals用于判定两个对象是否为同一对象,程序员得根据自己的需要重写这个方法来达到对象比较的目的。
hashcode是用来提高效率的,HashSet和HashMap是基于hashcode方法来查找对象的,所以你在使用这些类的时候一定要覆盖hashcode方法。
而非散列的Set和Map,例如TreeSet,TreeMap等,它们只需equals方法就可以唯一确定对象的身份。

解决方法:

为了使你写的类能够高效地应用于HashMap/HashTable,请重写hashcode方法。

FindBugs简单应用

Bug: "." used for regular expression
Pattern id: RE_POSSIBLE_UNINTENDED_PATTERN, type: RE, category:
CORRECTNESS

解释:

String的split方法传递的参数是正则表达式,正则表达式本身用到的字符需要转义,如:句点符号“.”,美元符号“$”,乘方符号“^”,大括号“{}”,方括号“[]”,圆括号“()” ,竖线“|”,星号“*”,加号“+”,问号“?”等等,这些需要在前面加上“\\”转义符。

解决方法:

在前面加上“\\”转义符。

FindBugs简单应用

Bug:
WindowHandlerManager$MySingleSelectionModel is Serializable; consider declaring
a serialVersionUID
Pattern id: SE_NO_SERIALVERSIONID, type: SnVI, category:
BAD_PRACTICE

解释:

实现了Serializable接口,却没有实现定义serialVersionUID字段,序列化的时候,我们的对象都保存为硬盘上的一个文件,当通过网络传输或者其他类加载方式还原为一个对象时,serialVersionUID字段会保证这个对象的兼容性,考虑两种情况:

  1. 新软件读取老文件,如果新软件有新的数据定义,那么它们必然会丢失。
  2. 老软件读取新文件,只要数据是向下兼容的,就没有任何问题。

序列化会把所有与你要序列化对象相关的引用(包括父类,特别是内部类持有对外部类的引用,这里的例子就符合这种情况)都输出到一个文件中,这也是为什么能够使用序列化能进行深拷贝。这种序列化算法给我们的忠告是,不要把一些你无法确定其基本数据类型的对象引用作为你序列化的字段(比如JFrame),否则序列化后的文件超大,而且会出现意想不到的异常。

解决方法:

定义serialVersionUID字段

FindBugs简单应用

Bug:
ToStringComparator implements Comparator but not Serializable
Pattern id: SE_COMPARATOR_SHOULD_BE_SERIALIZABLE, type:
Se, category: BAD_PRACTICE

解释:

ToStringComparator类实现了Comparator接口却没有实现Serializable接口。

Comparator提供了比较的一句,利用Collections.sort可完成排序。

为了能够序列化Treemap和TreeSet,那么它们所添加的类,如果实现了Comparators必须支持Serializable接口。

解决方法:

实现Serializable接口并定义serialVersionUID字段

FindBugs简单应用

Bug:
ManageItem defines equals(ManageItem) method and
uses Object.equals(Object)
Pattern id: EQ_SELF_USE_OBJECT, type: Eq, category:
CORRECTNESS

解释:

这是重载,不是覆盖,除非你能保证其他人调用这个方法传入的参数都是ManageItem 的,否则会调用Object的boolean equals(Object)方法,这样的话根本就不会跑到这个方法里来。

解决方法:

如果你想覆盖父类的方法,请在上面加上@Override注解,它会防止这种错误的出现。

FindBugs简单应用

Bug: Dead
store to date
Pattern id: DLS_DEAD_LOCAL_STORE, type: DLS, category:
STYLE

解释:

定义了却未使用的变量。

解决方法:

大胆的去掉或者注释掉。

FindBugs简单应用

Bug:
Doomed test for equality to NaN
Pattern id: FE_TEST_IF_EQUAL_TO_NOT_A_NUMBER, type: FE, category:
CORRECTNESS

解释:

Nan很特殊(表示未定义和不可表示的值),没有任何值跟它相等,包括它自身,所以x ==
Double.NaN永远返回false。

解决方法:

如果要检查x是特殊的,不是一个数值,请用Double.isNaN(x)方法。

FindBugs简单应用

Bug:
DBExportTask2.exportDBRecords(DBExportProperty, String) forces garbage
collection; extremely dubious except in benchmarking code
Pattern id: DM_GC, type: Dm, category:
PERFORMANCE

解释:

有两点:

  1. System.gc()只是建议,不是命令,JVM不能保证立刻执行垃圾回收。
  2. System.gc()被显示调用时,很大可能会触发Full GC

GC有两种类型:Scavenge GCFull GCScavenge GC一般是针对年轻代区(Eden区)进行GC,不会影响老年代和永生代(PerGen),由于大部分对象都是从Eden区开始的,所以Scavenge GC会频繁进行,GC算法速度也更快,效率更高。

但是Full GC不同,Full GC是对整个堆进行整理,包括Young、Tenured和Perm,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。

解决方法:

去掉System.gc()

FindBugs简单应用

Bug:
IPv4Document.m_strInitString isn't final but should be
Pattern id: MS_SHOULD_BE_FINAL, type: MS, category:
MALICIOUS_CODE

解释:

使用public和protected,别的包可以轻易修改它,如果你不想它被修改,请使用final。

封装很重要,不管是从维护方面和技术方面来说,都很重要,就算要给别人共享,也要提供get方法,特别是在并发环境中,get方法也应该是同步的。

解决方法:

加上final或者变为private的。

FindBugs简单应用

Bug:
ActionPatternManager.m_This should be package protected
Pattern id: MS_PKGPROTECT, type: MS, category:
MALICIOUS_CODE

解释:

Findbugs说,静态字段m_oThis应该是包权限的。

单例模式??这是神马单例模式?字段不是private,还是单例模式吗?

解决方法:

修改protected为private,然后将单例模式实现方式改为恶汉,或者双重校验锁定。

FindBugs简单应用

Bug:
Incorrect lazy initialization and update of static field
MonitorRuleManager.m_This
Pattern id: LI_LAZY_INIT_UPDATE_STATIC, type: LI, category:
MT_CORRECTNESS

解释:

这是一个多线程的BUG,多线程的问题之所以很严重,是因为我们很难复现解决它,但它又是的确存在的,它总是在关键时候爆发,让你感到很郁闷。

解决方法:

加上synchronized,改成饿汉模式,或者使用双重校验锁。

public class Singleton {

    private volatile static Singleton singleton;

      private Singleton (){}

      public static Singleton getSingleton() {

      if (singleton == null) {

        synchronized (Singleton.class) {

              if (singleton == null) {

singleton = new Singleton();

}

}

}

return singleton;

}

}

FindBugs简单应用

Bug:
Method JTAMainFrame.initView(JFrame) makes inefficient use of keySet iterator
instead of entrySet iterator
Pattern id: WMI_WRONG_MAP_ITERATOR, type: WMI, category:
PERFORMANCE

解释:

很多人都这样遍历Map,没错,但是效率很低,先一个一个的把key遍历,然后在根据key去查找value,这不是多此一举么,为什么不遍历entry(桶)然后直接从entry得到value呢?它们的执行效率大概为1.5:1(有人实际测试过)。

解决方法:

  for (Map.Entry<String, JMenu> entry : menuList.entrySet()) {

       mb.add(entry.getValue());

}

FindBugs简单应用

Bug:
SingleNePollConfigDialog.collectValues(Hashtable) may expose internal
representation by storing an externally mutable object into
SingleNePollConfigDialog.values
Pattern id: EI_EXPOSE_REP2, type: EI2, category:
MALICIOUS_CODE

解释:

参数values保存在当前线程的执行栈中,而this.values保存在堆上,它们同时指向同一个对象,对参数values的任何操作都会影响到this.values,如果你知道这一点,而且本意就是这样的,那么你可以忽略上面这些话,但是下面这些话你应该好好听听。

这是一段正确的代码,但不是一段可维护性强、可理解性强的代码,参数代表操作的条件,它们应该是只读的,我们不应该对它直接进行操作或者赋值。

解决方法:

如果把上面对参数values的操作都改成this.values。

FindBugs简单应用

Bug:
temsLoader.getItemsWithPriority() may expose internal representation by
returning ItemsLoader.m_htItemsWithPriority
Pattern id: EI_EXPOSE_REP, type: EI, category:
MALICIOUS_CODE

解释:

刚开始一看挺纳闷的,这个方法有什么问题吗?某些数据集合不应该直接对外提供public返回方法,提供了get方法,但实际上可以任意修改里面的数据。

解决方法:

如果你确定这些数据集合不应该被外界修改,那么对于基本数据类型,你提供get方法即可,对于引用,get方法里的返回值应该是数据的拷贝。

FindBugs简单应用

Bug:
Method call passes null for nonnull parameter of queryScriptData(ObjService)
Pattern id: NP_NULL_PARAM_DEREF, type: NP, category:
CORRECTNESS

解释:

当getAllListFiles方法发生了任何异常(checked和unchecked),allFiles都为null,关键是在queryScriptData方法里,并没有对参数是否为null进行判断,它直接调用了参数对象上面的方法,这肯定会发生空指针异常。

一个优秀的程序员,在过马路时都要向两边看一下,在写一个方法时,首先要考虑的就是对方法参数的有效性判断。

解决方法:

在queryScriptData方法里对参数进行有效性判断。

FindBugs简单应用

Bug:
TopoCardManagerAction.processLocalCard(Hashtable) invokes inefficient Boolean
constructor; use Boolean.valueOf(...) instead
Pattern id: DM_BOOLEAN_CTOR, type: Dm, category:
PERFORMANCE

解释:

不必创建一个新的Boolean对象,使用Boolean.valueOf方法可以重用Boolean.FALSE和Boolean.TRUE对象。

我们可以从API中可以看到public Boolean(boolean value)方法的解释:注:一般情况下都不宜使用该构造方法。若不需要新 的实例,则静态工厂 valueOf(boolean) 通常是一个更好的选择。这有可能显著提高空间和时间性能。

解决方法:

使用Boolean.valueOf方法或者直接返回Boolean.FALSE和Boolean.TRUE对象。