FindBugs 错误分析说明


FindBugs 错误修改指南 版本号 修改描述 作者 日期 1.0 新建 cantellow 2011-5-24 1.1 补充英文标识及出错案例 卫缺 2011-6-9 使用方法:复制你的 Pattern id,然后用快捷键 Ctrl + F 查找,即可快速定位。 所有的解释和解决方式只是可能和建议,大多数时候需要根据实际情况而定。 Findbugs 也有少量的误报,需要大家自己甄别。 1. EC_UNRELATED_TYPES Bug: Call to equals() comparing different types Pattern id: EC_UNRELATED_TYPES, type: EC, category: CORRECTNESS 解释: 两个不同类型的对象调用 equals 方法,如果 equals 方法没有被重写,那么调用 object 的==, 永远不会相等;如果 equals 方法被重写,而且含有 instanceof 逻辑,那么还是不会相等。 解决方法: 应该改为 str.toString() 2. IM_BAD_CHECK_FOR_ODD 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 3. NP_ALWAYS_NULL Pattern: Null pointer dereference id: NP_ALWAYS_NULL, type: NP, category: CORRECTNESS A null pointer is dereferenced here. This will lead to a NullPointerException when the code is executed. 4. RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE Bug: Redundant nullcheck of bean1, which is known to be non-null Pattern id: RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE, type: RCN, category: STYLE This method contains a redundant check of a known non-null value against the constant null. 这种方法包含了一个称为非空对空值的不断重复检查。 修改为: 5. SS_SHOULD_BE_STATIC Bug: Unread field: ADDRESS_KEY; should this field be static? Pattern id: SS_SHOULD_BE_STATIC, type: SS, category: PERFORMANCE This class contains an instance final field that is initialized to a compile-time static value. Consider making the field static. 解释: final 成员变量表示常量,只能被赋值一次,赋值后值不再改变。 这个类包含的一个 final 变量初始化为编译时静态值。考虑变成静态常量 解决方法: 增加 static 关键字 6. EQ_COMPARETO_USE_OBJECT_EQUALS Bug: RsInterface defines compareTo(Object) and uses Object.equals() Pattern id: EQ_COMPARETO_USE_OBJECT_EQUALS, type: Eq, category: BAD_PRACTICE 解释: 第一段代码,没有使用 instanceof 判断就直接转型,有抛出 classcastexception 异常的可能。 这个 BUG 主题是,遵守约定(x.compareTo(y)==0) == (x.equals(y)),强烈建议,但不严格要求。 在 return 0 的时候,调用 equals 方法返回 true,因为在 PriorityQueue.remove 方法中,1.5 使用的是 compareTo 方法,而 1.6 使用的是 equals 方法,保证环境升级的时候,受影响最小。 解决方法: 在 return 0 的时候,调用 equals 方法返回 true 7. NM_METHOD_NAMING_CONVENTION Bug: The method name MsmPlanDAOTest.TestViewMsmPlanList() doesn't start with a lower case letter Pattern id: NM_METHOD_NAMING_CONVENTION, type: Nm, category: BAD_PRACTICE Methods should be verbs, in mixed case with the first letter lowercase, with the first letter of each internal word capitalized. 解释: 方法应该是动词,与第一个字母小写混合的情况下,与每个单词的首字母大写的内部。 解决方法: 方法名称小写就通过了。 8. HE_EQUALS_USE_HASHCODE 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 一定相等;hashcode 相等,equals 不一定相等。”除非你保证不运 用到 HashMap/HashTable 等运用散列表查找值的数据结构中。否则,发生任何事情都是有可 能的。 关于何时改写 hashcode,请参考:在重写了对象的 equals 方法后,还需要重写 hashCode 方法吗? 关于编写高质量的 equals 方法: 1.先使用==操作符检查是否是同一个对象,==都相等,那么逻辑相等肯定成立; 2.然后使用 instanceof 操作符检查“参数是否为正确的类型”; 3.把参数转换成正确的类型; 4.对于该类中的非基本类型变量,递归调用 equals 方法; 5.变量的比较顺序可能会影响到 equals 方法的性能,应该最先比较最有可能不一致的变 量,或者是开销最低的变量。 当你编写完成 equals 方法之后,应该问自己三个问题:它是否是对称的、传递的、一 致的? 解决方法: 除非你保证不运用到 HashMap/HashTable 等运用散列表查找值的数据结构中,请重写 hashcode 方法。 9. NM_CONFUSING Bug: Confusing to have methods xxx.SellerBrandServiceImpl.getAllGrantSellerBrandsByBrandId(long) and xxx.DefaultSellerBrandManager.getALLGrantSellerBrandsByBrandId(long) Pattern id: NM_CONFUSING, type: Nm, category: BAD_PRACTICE The referenced methods have names that differ only by capitalization. 解释: 同一个包两个类中有一模一样的两个方法(包括参数) 解决方法: 最好可以修改为不一样的方法名称 10. MF_CLASS_MASKS_FIELD Bug: Field PDHSubCardInstanceDialogCommand.m_instance masks field in superclass ViewNEProperity Pattern id: MF_CLASS_MASKS_FIELD, type: MF, category: CORRECTNESS This class defines a field with the same name as a visible instance field in a superclass. This is confusing, and may indicate an error if methods update or access one of the fields when they wanted the other. 解释: 这是什么意思呢?想要字段也能够具有多态性吗?太迷惑了。 当你想要更新一个 m_instance 时,你要更新哪个?你用到它时,你知道哪个又被更新了? 解决方法: 要么去掉其中一个字段,要么重新命名。 11. NM_CLASS_NAMING_CONVENTION Bug: The class name crossConnectIndexCollecter doesn't start with an upper case letter 解释: Pattern id: NM_CLASS_NAMING_CONVENTION, type: Nm, category: BAD_PRACTICE 看到这样的命名方式,我第一个反映就是有点晕车! 解决方法: 类名第一个字符请大写。 12. RE_POSSIBLE_UNINTENDED_PATTERN Bug: "." used for regular expression Pattern id: RE_POSSIBLE_UNINTENDED_PATTERN, type: RE, category: CORRECTNESS 解释: String 的 split 方法传递的参数是正则表达式,正则表达式本身用到的字符需要转义,如:句 点符号“.”,美元符号“$”, 乘方符号“^”, 大括号“{}”,方括号“[]”,圆括号“()” ,竖 线“|”,星号“*”,加号“+”,问号“?”等等,这些需要在前面加上“\\”转义符。 解决方法: 在前面加上“\\”转义符。 13.IA_AMBIGUOUS_INVOCATION_OF_INHERITED_OR_OU TER_METHOD 外部类: 内部类: …… Bug: Ambiguous invocation of either an outer or inherited method JExtendDialog.onOK() Pattern id: IA_AMBIGUOUS_INVOCATION_OF_INHERITED_OR_OUTER_METHOD, type: IA, category: STYLE 解释: TargetSetupDialog是JExtendDialog的子类,JExtendDialog有一个onOK方法,但是JExtendDialog 的外部类也有一个 onOK 方法,到底这个 onOK 方法调用的是它父类 onOK 方法还是调用它 外部类 onOK 方法呢,这不免让人误解。 当然这并没有编译错误,实际上优先调用的是父类 JExtendDialog 的 onOK 方法,如果把 JExtendDialog 的 onOK 方法去掉,它调用的就是外部类 onOK 方法,这个时候不能写成 this.onOK,因为此时的 this 并不代表外部类对象。 解决方法: 如果要引用外部类对象,可以加上“outclass.this”。 如果要引用父类的 onOK 方法,请使用 super.onOK()。 14. DM_FP_NUMBER_CTOR Bug: Method OnlineLicenseDAOTest.testUpdateOnlineLicenseByOnlineMerchantId() invokes inefficient Double.valueOf(double) constructor; use OnlineLicenseDAOTest.java:[line 81] instead Pattern id: DM_FP_NUMBER_CTOR, type: Bx, category: PERFORMANCE Using new Double(double) is guaranteed to always result in a new object whereas Double.valueOf(double) 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. Unless the class must be compatible with JVMs predating Java 1.5, use either autoboxing or the valueOf() method when creating instances of Double and Float. 解释: 采用 new Ddouble(double)会产生一个新的对象,采用 Ddouble.valueOf(double)在编译的时候 可能通过缓存经常请求的值来显著提高空间和时间性能。 解决方法: 采用 Ddouble.valueOf 方法 类似的案例 15. CN_IMPLEMENTS_CLONE_BUT_NOT_CLONEABLE Bug:AlarmSoundManager$SoundProperty defines clone() but doesn't implement Cloneable Pattern id: CN_IMPLEMENTS_CLONE_BUT_NOT_CLONEABLE, type: CN, category: BAD_PRACTICE 解释: SoundProperty 类实现了 clone 方法,但是没有实现 Cloneable 接口,当然这没有任何问题, 但是你应该知道你为什么这么做。 解决方法: 最好实现 Cloneable 接口 16. STCAL_INVOKE_ON_STATIC_DATE_FORMAT_INSTANCE Bug: Call to method of static java.text.DateFormat Pattern id: STCAL_INVOKE_ON_STATIC_DATE_FORMAT_INSTANCE, type: STCAL, category: MT_CORRECTNESS 解释: TIME_FORMAT 是一个 DateFormat 静态变量,文档中 DateFormat 不是线程安全(多个线程访 问一个类时,这些线程执行顺序没有统一的调度和约定,如果这个类的行为仍然是正确的, 那么这个类就是线程安全的。考虑 vector 的实现)的,如果多个线程同时访问,会出现意 料不到的情况,详情参见 Sun Bug #6231579 和 Sun Bug #6178997。 因此对于 DateFormat、SimpleDateFormat、Calendar 类对象不建议定义成静态成员字段使用, 同时对它们在多线程环境下的使用请一定要保证同步。 另外,多说一句,java 为我们提供了很多的封装手段,比如 private 关键字、内部类、全限 定包名等等,我们要充分利用这些手段封装信息,对外尽量提供最小集。关于静态变量也是 如此,就算是 vector 这种线程安全的类,在无状态类中也可能存在并发的问题,参见:无 状态类在并发环境中绝对安全吗? 解决方法: 修改类字段为对象字段,然后改为 private,同时提供 get 方法,最后对 get 方法实现同步机 制。 最好连对象字段也去掉,直接在方法里使用,就不存在同步的问题了(不必考虑性能问题, 而且 DateFormat 本身就不必作为对象的字段,我想这也是 sun 为什么不把它实现为线程安 全的了)。 17. SE_NO_SERIALVERSIONID Bug: WindowHandlerManager$MySingleSelectionModel is Serializable; consider declaring a serialVersionUID Pattern id: SE_NO_SERIALVERSIONID, type: SnVI, category: BAD_PRACTICE This class implements the Serializable interface, but does not define a serialVersionUID field. A change as simple as adding a reference to a .class object will add synthetic fields to the class, which will unfortunately change the implicit serialVersionUID (e.g., adding a reference to String.class will generate a static field class$java$lang$String). Also, different source code to bytecode compilers may use different naming conventions for synthetic variables generated for references to class objects or inner classes. To ensure interoperability of Serializable across versions, consider adding an explicit serialVersionUID. 解释: 实现了 Serializable 接口,却没有实现定义 serialVersionUID 字段,序列化的时候,我们的对 象都保存为硬盘上的一个文件,当通过网络传输或者其他类加载方式还原为一个对象时, serialVersionUID 字段会保证这个对象的兼容性,考虑两种情况: 1. 新软件读取老文件,如果新软件有新的数据定义,那么它们必然会丢失。 2. 老软件读取新文件,只要数据是向下兼容的,就没有任何问题。 序列化会把所有与你要序列化对象相关的引用(包括父类,特别是内部类持有对外部类的引 用,这里的例子就符合这种情况)都输出到一个文件中,这也是为什么能够使用序列化能进 行深拷贝。这种序列化算法给我们的忠告是,不要把一些你无法确定其基本数据类型的对象 引用作为你序列化的字段(比如 JFrame),否则序列化后的文件超大,而且会出现意想不到 的异常。 解决方法: 定义 serialVersionUID 字段 18.SE_COMPARATOR_SHOULD_BE_SERIALIZABLE Bug: ToStringComparator implements Comparator but not Serializable Pattern id: SE_COMPARATOR_SHOULD_BE_SERIALIZABLE, type: Se, category: BAD_PRACTICE 解释: ToStringComparator 类实现了 Comparator 接口却没有实现 Serializable 接口,因为像 TreeMap 这种可序列化数据结构(它实现了 Serializable 接口)只有当比较器继承了 Serializable 接口 时,它才能被序列化。 解决方法: 实现 Serializable 接口并定义 serialVersionUID 字段 19. ES_COMPARING_STRINGS_WITH_EQ Bug: Comparison of String objects using == or != Pattern id: ES_COMPARING_STRINGS_WITH_EQ, type: ES, category: BAD_PRACTICE 解释: 你确定你已经了解 string 的全部了? 如果你不了解,请参考 FX 大神的博文:请别再拿“String s = new String("xyz");创建了多少个 String 实例”来面试了吧 那么,接下来我就开始剥皮了: Object 和 StringBuilder 的 toString 方法都是返回一个 new String(),跟””不相等。 如果你之前是这样的定义的:String name = “”;OK,它们处于同一个 class 常量池,跟””相等。 如果在这之前,你使用了 String. Intern 方法,你是高手,跟””相等。 如果你没有意识到这些问题,却仍然使用==和!=去比较字符串,那么请不要告诉我是你手滑 了= =! 解决方法: 老实使用 equals 方法吧,至少为了保持代码的清晰性。 20. ES_COMPARING_STRINGS_WITH_EQ Bug: Comparison of String parameter using == or != Pattern id: ES_COMPARING_PARAMETER_STRING_WITH_EQ, type: ES, category: BAD_PRACTICE 解释: 跟前面的例子差不多,你如果不能确保 propertyName 来源于常量池,那么用==比较没有一 点意义,难不成你告诉我这能提高性能? 如果有功夫为这点性能担惊受怕,还不如花点时 间去找找性能瓶颈。 解决方法: 使用 equals 方法 21. IM_AVERAGE_COMPUTATION_COULD_OVERFLOW Bug: Computation of average could overflow Pattern id: IM_AVERAGE_COMPUTATION_COULD_OVERFLOW, type: IM, category: STYLE 解释: 参照了 Findbugs 的解释,(low+high)/2 当平均数过大的时候(难道是超过了 int 最大值?) 会溢出,会出现一个负值,此问题出现在早期实现的二进制搜索和归并排序,但是已经被修 复了。参见 Joshua Bloch(google 首席 java 架构师)widely publicized the bug pattern(需翻 墙). 解决方法: 建议使用无符号右移位运算符:use (low+high) >>> 1 22. SC_START_IN_CTOR Bug: new AsyncCentral() invokes AsyncCentral$FireThread.start() Pattern id: SC_START_IN_CTOR, type: SC, category: MT_CORRECTNESS 解释: 构造方法里重启新的线程,我还是第一次见过这样写的。 首先说明三点: 1. 对象的创建一般分两步走,在堆上 new 对象操作,执行方法(包含构造方法),为 什么我们开发人员看见的只有一步,那是因为 JVM 不想让开发人员在这个过程中插上一 脚,破坏对象的初始化流程。 2. 类的加载和初始化是由虚拟机保证同步的,但是对象的生成和初始化就没有任何同步机 制来保证了。 3. 构造器不能加 synchronized,是一项程序语言设计上的选择(见:JLS 8.8.3 Constructor Modifiers),正常情况下,是不需要加上 synchronized,但不代表所有的情况都不要加上 synchronized,更不能认为一个构造器隐含的就是一个 synchronized。 那什么时候构造方法需要同步呢?通常来说,方法在生成对象的时候只被执行一次, 一般 new 对象的操作可能因为 JVM 自身的关系保证原子性操作(自己臆测的,没有任何根 据),所以我们经常不用关心构造方法同步的问题。但是上述情况就不一样了,在构造方法 中新启线程,如果 AsyncCentral 是一个状态类,FireThread 线程极有可能对 AsyncCentral 的 状态进行反复读取和写入,更严重的一种情况是,AsyncCentral 有父类,极有可能在父类的 构造方法还没开始前,FireThread 线程就已经开始执行并对 AsyncCentral 的状态进行“破坏” 了。这个时候,就有两个线程来对 AsyncCentral 的状态进行操作了(一个是执行方法 的线程,一个是 FireThread 线程),自然而然,就会存在同步的问题了。 多数时候,我们没有发现,可能是 AsyncCentral 类没有状态,或者是时候未到,我想说的是, 我们写的大部分程序都存在同步的问题,本例子就是其中一个,值得我们好好思考。 另一种理解(觉得更靠谱,来自于 Java.Concurrency.in.Practice)叫做“对象逃逸”,意思就 是说在构造方法里,this 是可以访问的到的,同一时间,FireThread 线程而是可以访问到 this 对象的,所以这时候 this 就从方法线程逃逸到了 FireThread 线程中,这时候初始化就 会存在并发问题。 解决方法: 不要再构造方法中新启线程,可以提供 init 方法,其他方法根据实际情况而定。 23. EQ_SELF_USE_OBJECT 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 注解,它会防止这种错误的出现(透露 一个小细节,JDK1.5 覆盖接口方法时加上@Override 编译器会报错,JDK1.6 修正,这可能是 当初实现者对@Override 注解理解的问题)。 24.DLS_DEAD_LOCAL_STORE 案例二: Bug: Dead store to date Pattern id: DLS_DEAD_LOCAL_STORE, type: DLS, category: STYLE 解释: 先看看,我们的程序有多少个这样的例子: 真是伤不起啊,不知道当时的作者这是神马意图?手滑?还是眼花?虽然说这不是神马问题, 也不会对程序性能造成多大的影响,但是这就像一颗沙子,我们每个程序员对待程序都应该 是眼里不能进沙子的态度,当然,你非要这么写,我也没神马可说的。 By the way: 对本地变量定义了之后未使用到,编译器能够做优化处理,也就是在编译之后的 class 文件 中删除这些本地变量。方法是在 eclipse 的 Preferences 里将以下的钩去除: 解决方法: 大胆的去掉或者注释掉。 误报的案例: 上述案例二种: IntegralItemDO integralItem = new IntegralItemDO(); 是一个局部的变量,不需要定义到外部去,定义在外部,可能会变成一个无效的变量。 25.FE_TEST_IF_EQUAL_TO_NOT_A_NUMBER Bug: Doomed test for equality to NaN Pattern id: FE_TEST_IF_EQUAL_TO_NOT_A_NUMBER, type: FE, category: CORRECTNESS 解释: 我也开眼界了,照搬 Findbugs 的理解: 大概意思就是说 Nan 很特殊(表示未定义和不可表示的值),没有任何值跟它相等,包括它 自身,所以 x == Double.NaN 永远返回 false。 解决方法: 如果要检查 x 是特殊的,不是一个数值,请用 Double.isNaN(x)方法。 26. FI_EMPTY Bug: FilterIPConfigDialog.finalize() is empty and should be deleted Pattern id: FI_EMPTY, type: FI, category: BAD_PRACTICE 解释: 空的 finalize 方法,有什么用? 根据 JDK 文档, finalize() 是一个用于释放非 Java 资源的方法。但是, JVM 有很大的可能不 调用对象的 finalize() 方法,因此很难证明使用该方法释放资源是有效的。 解决方法: 删除掉 finalize 方法 27.REC_CATCH_EXCEPTION Bug: Exception is caught when Exception is not thrown Pattern id: REC_CATCH_EXCEPTION, type: REC, category: STYLE 解释: 我觉得有点迷惑,有些 catch (Exception e)并没有被 Findbugs 捕捉到,开始以为它的意思是 try catch 里没有任何异常的产生,包括 RuntimeException,但是后来我写了例子证明并不是 这么回事。 总之,它的意思应该是说 JVM 对 RuntimeException 有统一的捕获机制(一般都是打印异常 栈信息,然后向外抛,没有遇到 Exception 线程就死掉,EDT 线程除外),你搞一个 catch (Exception e)这样也把 RuntimeException 就捕获了。但是如果你的处理机制中没有针对这些 异常,那就可能有问题了。通常来说,很多应用程序都把异常记录在日志之中,但是我觉得 也应该同时打印在调试屏幕中,这样有利于开发人员调试。 比如上面的程序,假如发生了空指针异常,你只有去日志中才能看到,这对我们调试人员来 说很不方便的。 解决方法: 其实这样写也没有问题(除非你有意),有时候我们确实需要捕获 RuntimeException,比如 我们有一个批处理,这个任务很重要,必须保证某个任务出了问题不能影响其他的任务,这 个时候就可以在 for 循环内捕获 RuntimeException,出现了异常还可以 continue。 不过上面的例子最好再把异常信息打印到调试屏幕上。 28. DM_GC 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 GC 和 Full GC,Scavenge GC 一般是针对年轻代区(Eden 区)进行 GC,不会影响老年代和永生代(PerGen),由于大部分对象都是从 Eden 区开始的,所以 Scavenge GC 会频繁进行,GC 算法速度也更快,效率更高。但是 Full GC 不同,Full GC 是对 整个堆进行整理,包括 Young、Tenured 和 Perm,所以比 Scavenge GC 要慢,因此应该尽可 能减少 Full GC 的次数。 解决方法: 去掉 System.gc() 28. DP_DO_INSIDE_DO_PRIVILEGED Bug: com.taobao.sellerservice.core.test.BaseTestJunit.autoSetBean() invokes reflect.Field.setAccessible(boolean), which should be invoked from within a doPrivileged block Pattern id: DP_DO_INSIDE_DO_PRIVILEGED, type: DP, category: BAD_PRACTICE This code invokes a method that requires a security permission check. If this code will be granted security permissions, but might be invoked by code that does not have security permissions, then the invocation needs to occur inside a doPrivileged block. 此代码调用一个方法,需要一个安全权限检查。如果此代码将被授予安全权限,但可能是由 代码不具有安全权限调用,则需要调用发生在一个 doPrivileged 的块。 30. MS_SHOULD_BE_FINAL Bug: IPv4Document.m_strInitString isn't final but should be Pattern id: MS_SHOULD_BE_FINAL, type: MS, category: MALICIOUS_CODE 解释: 使用 public 和 protected,别的包可以轻易修改它,如果你不想它被修改,请使用 final。 封装很重要,不管是从维护方面和技术方面来说,都很重要,我不明白为神马有那么多的人 把变量都写成 public 的(就算要给别人共享,也要提供 get 方法),特别是在并发环境中, 特别特别注意类变量的共享,而且特别特别特别注意共享的这个变量是否是线程安全的。 解决方法: 加上 final 31. NM_FIELD_NAMING_CONVENTION Bug: The field name TopoControlPaneII.SyncSelection doesn't start with a lower case letter Pattern id: NM_FIELD_NAMING_CONVENTION, type: Nm, category: BAD_PRACTICE 解释: 为神马字段是大写开头的?喂神马?喂神马啊? 解决方法: 建议按照 sun 规定的命名方式 Bug: Field only ever set to null: RaisecomStatus.infoURL Pattern id: UWF_NULL_FIELD, type: UwF, category: CORRECTNESS 解释: 字段 infoURL 整个过程中一直为 null,但却被用来作为分支判断的条件,不知道作者何意? 难道真的是传说中的手滑? 解决方法: 这个就要问作者的意图了,当时你究竟要干神马来着? 32. MS_PKGPROTECT Bug: ActionPatternManager.m_This should be package protected Pattern id: MS_PKGPROTECT, type: MS, category: MALICIOUS_CODE 解释: Findbugs 说,静态字段 m_oThis 应该是包权限的,如果是 protected 的话,可以被其他包访 问到,其实个人觉得仅仅是封装范围的话是一个“小问题”,毕竟很多人都没意识到 public、 protected 等关键字的重要性。但是我接着往下看: 单例模式??这是神马单例模式?字段不是 private,还是单例模式吗?我在任何一个地方 继承 UserManager,然后直接 m_oThis = new UserManager();这还是一个单例吗? 在看看 Findbugs 为我们找出了多少个: 另外,我很客观的说一点,我们后怕,因为知道了真相,在想想我们实际情况中遇到很多不 能复现的问题,我们有理由去知道这一切。 解决方法: 修改 protected 为 private,然后将单例模式实现方式改为恶汉,或者双重校验锁定。 33. FI_USELESS Pattern: Finalizer does nothing but call superclass finalizer id: FI_USELESS, type: FI, category: BAD_PRACTICE 解释: finalize() 是一个用于释放非 Java 资源的方法,这里的 finalize 直接用 Object 的 finalize 方法, 无任何意义。 解决方法: 勇敢去掉 finalize() 34. NP_NULL_ON_SOME_PATH Bug: Possible null pointer dereference of busCatId Pattern id: NP_NULL_ON_SOME_PATH, type: NP, category: CORRECTNESS 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. 解释: 方法中存在空指针 解决方法: 增加字段 busCatId 为空的判断 35. NP_NULL_ON_SOME_PATH Bug:.HierarchicalManagerImpl.isExistByName(String, long) forgets to throw new exception.HierarchicalServiceException(String, Throwable) Pattern id: RV_EXCEPTION_NOT_THROWN, type: RV, category: CORRECTNESS This code creates an exception (or error) object, but doesn't do anything with it. For example, something like if (x < 0) new IllegalArgumentException("x must be nonnegative"); It was probably the intent of the programmer to throw the created exception: if (x < 0) throw new IllegalArgumentException("x must be nonnegative"); 解释: 此代码创建了一个异常(或错误)的对象,但并不做任何事情。 可能作者是想继续抛出异常信息吧,可是却产生了一个对象,啥也不干。 解决方法: 抛出这个错误 36. FI_FINALIZER_NULLS_FIELDS Bug: CustomerResTreeDialog.java:[line 67] is set to null inside finalize method Pattern id: FI_FINALIZER_NULLS_FIELDS, type: FI, category: BAD_PRACTICE 解释: 关于 finalize 方法,前面应该已经介绍过了,所以 m_UniResTree = null,纯粹是多此一举,没 有任何意义。 解决方法: 勇敢去掉 finalize() 37. FI_PUBLIC_SHOULD_BE_PROTECTED Bug: FilterIPConfigDialog.finalize() is public; should be protected Pattern id: FI_PUBLIC_SHOULD_BE_PROTECTED, type: FI, category: MALICIOUS_CODE 解释: Finalize 方法不是 protected 的,当然你写成 public 也没错,依然可以覆盖父类中的 finalize 方法。 解决方法: 勇敢去掉 finalize() 38. IS2_INCONSISTENT_SYNC Bug: Inconsistent synchronization of URLAlarmMonitor.m_Counter; locked 50% of time Pattern id: IS2_INCONSISTENT_SYNC, type: IS, category: MT_CORRECTNESS 解释: m_Counter 只锁住了 50%,它还是处于线程不安全的状态,如果一个字段只被 read,那么它 是线程安全的,不需要提供额外的同步开销,可以定义为 final 的(参考不可变类的实现), 如果既有 read 也有 write,那么就必须保证每个 get 和 set 方法都同步,而不能像上面一样, 只对 set 方法进行了同步。 解决方法: 对 get 和 set 方法都实行同步。 39. LI_LAZY_INIT_UPDATE_STATIC Bug: Incorrect lazy initialization and update of static field MonitorRuleManager.m_This Pattern id: LI_LAZY_INIT_UPDATE_STATIC, type: LI, category: MT_CORRECTNESS 解释: 此问题的 m_This 也是 protected 的,这里就不再追究了。这里的问题是,当线程 1 执行到 initMonitorRules 方法时,线程 2 执行 getInstance 方法,它直接返回 m_This,这时候它可以 用 m_This 做任何事情,但是此时线程 1 的初始化动作还没有完成,如果 initMonitorRules 方 法里有对对象状态进行更新的操作,那么很可能线程 2 得到的对象的状态是还没有初始化的, 这就是一个多线程的 BUG(多线程的问题之所以很严重,是因为我们很难复现解决它,但 它又是的确存在的,它总是在关键时候爆发,让你感到很郁闷)! 当然就算没有 initMonitorRules 方法,这个单例模式也不是线程安全的,下面会讲到这个问 题。 解决方法: 将 initMonitorRules 方法放在构造方法里,然后将单例改成恶汉模式,或者使用双重校验锁。 40. LI_LAZY_INIT_STATIC Bug: Incorrect lazy initialization of static field TopoController.m_This Pattern id: LI_LAZY_INIT_STATIC, type: LI, category: MT_CORRECTNESS 解释: 为什么它存在多线程的 bug,比如线程 1 进入到 if 语句内,被线程 2 打断,线程 2 同样进入 了 if 语句内然后生成了一个对象 a,随即被线程 1 打断,线程 1 又生成了另外一个对象 b, 这还是一个单例么? 更详细的解释请看:双重检查锁定以及单例模式 另外,关于单例模式更多的资料,参见单例模式的七种写法 如果你并发功底相当好,请看这篇文章:用 happen-before 规则重新审视 DCL 解决方法: 我比较钟情于恶汉,如果需要传递参数,我会使用双重校验锁。 41. WMI_WRONG_MAP_ITERATOR 案例一: 案例二: 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 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. 解释: 很多人都这样遍历 Map,没错,但是效率很低,先一个一个的把 key 遍历,然后在根据 key 去查找 value,这不是多此一举么,为什么不遍历 entry(桶)然后直接从 entry 得到 value 呢?它们的执行效率大概为 1.5:1(有人实际测试过)。 我们看看 HashMap.get 方法的源代码: 1. public V get(Object key) { 2. if (key == null) 3. return getForNullKey(); 4. int hash = hash(key.hashCode()); 5. for (Entry e = table[indexFor(hash, table.length)]; 6. e != null; 7. e = e.next) { 8. Object k; 9. if (e.hash == hash && ((k = e.key) == key || key.equals(k))) 10. return e.value; 11. } 12. return null; 13. } 从这里可以看出查找 value 的原理,先计算出 hashcode,然后散列表里取出 entry,不管是 计算 hashcode,还是执行循环 for 以及执行 equals 方法,都是 CPU 密集运算,非常耗费 CPU 资源,如果对一个比较大的 map 进行遍历,会出现 CPU 迅速飚高的现象,直接影响机器的 响应速度,在并发的情况下,简直就是一场灾难。 解决方法: 1. for (Map.Entry entry : menuList.entrySet()) { 2. mb.add(entry.getValue()); } for(Map.Entry> tempEntiy: companyBlackItemsMap.entrySet()) { String key = tempEntiy.getKey(); List eachCompanyBlackItems = tempEntiy.getValue(); 42. BC_VACUOUS_INSTANCEOF Bug: instanceof will always return true, since all TopoTreeNode are instances of TopoTreeNode Pattern id: BC_VACUOUS_INSTANCEOF, type: BC, category: STYLE 解释: 因为 getSelectedTreeNode 方法返回类型就是 TopoTreeNode,所以 if 里的 instanceof 测试永 远为 true,除非它是 null,确保你没有其他的逻辑上的误解,你这样写,会让其他人丈二和 尚摸不着头脑。 解决方法: 去掉 instanceof 检测。 43. INT_BAD_REM_BY_1 Bug: Integer remainder modulo 1 computed Pattern id: INT_BAD_REM_BY_1, type: INT, category: STYLE 解释: I % 1 永远都为 0,I / 1 也为 i,不知道作者想干嘛。 解决方法: 恕我愚昧,不明白作者的意图。 Bug: Load of known null value Pattern id: NP_LOAD_OF_KNOWN_NULL_VALUE, type: NP, category: STYLE The variable referenced at this point is known to be null due to an earlier check against null. Although this is valid, it might be a mistake (perhaps you intended to refer to a different variable, or perhaps the earlier check to see if the variable is null should have been a check to see if it was nonnull). 解释: Node 为 null,还进一步调用它上面的方法,除非你能保证当 node 为 null 的时候 isDeleteSingleObject 为 false,否则很可能发生空指针异常,我估计作者是第二个 if 是想判断 node != null 吧。 解决方法: 努力找到原作者,当面询问其用意。 44. EI_EXPOSE_REP2 案例 DO 类 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,我相信你和你的同事都会觉得这样的代 码更加清晰。 } 案例二 DO 类 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 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. 翻译愿意: 此代码存储到一个到对象的内部表示外部可变对象的引用。如果实例是由不受信任的代码, 并以可变对象会危及安全或其他重要的属性选中更改访问,你需要做不同的东西。存储一个 对象的副本,在许多情况下是更好的办法。 解释: DO 类实例产生之后,里面包含的 Date 不是原始数据类型,导致其 gmtCrate 属性不光是 set 方法可以改变其值,外部引用修改之后也可能导致 gmtCreate 被改变,会引起可能的不 安全或者错误。 这个是一个不好的实践,不过我们应用里面 DO 都是比较简单使用,不太会出现这种情况。 解决方法: 修改成: public Date getGmtCreate() { return new Date(this.gmtCreate.getTime()); //正确值 } 45. EI_EXPOSE_REP Bug: temsLoader.getItemsWithPriority() may expose internal representation by returning ItemsLoader.m_htItemsWithPriority Pattern id: EI_EXPOSE_REP, type: EI, category: MALICIOUS_CODE 解释: 刚开始一看挺纳闷的,这个方法有什么问题吗?后来仔细看一下,发现返回值都有一个特点, 它们都是集合数组之类的,我想 findBugs 的本意是,某些数据集合不应该直接对外提供 public 返回方法,即使表面上提供了 get 方法,但实际上可以任意修改里面的数据。 解决方法: 如果你确定这些数据集合不应该被外界修改,那么对于基本数据类型,你提供get方法即可, 对于引用,get 方法里的返回值应该是数据的拷贝。 46. NP_NULL_PARAM_DEREF 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 方法里对参数进行有效性判断。 46. SBSC_USE_STRINGBUFFER_CONCATENATION Bug: Method InitDBPoolParaTask.execute() concatenates strings using + in a loop Pattern id: SBSC_USE_STRINGBUFFER_CONCATENATION, type: SBSC, category: PERFORMANCE 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. 解释: 每次循环里的字符串+连接,都会新产生一个 string 对象,在 java 中,新建一个对象的代价 是很昂贵的,特别是在循环语句中,效率较低。 解决方法: 利用 StringBuffer 或者 StringBuilder 重用对象。 47. RV_RETURN_VALUE_IGNORED_BAD_PRACTICE Bug: NewScriptAction.actionPerformed(ActionEvent) ignores exceptional return value of java.io.File.delete() Pattern id: RV_RETURN_VALUE_IGNORED_BAD_PRACTICE, type: RV, category: BAD_PRACTICE 解释: 关于一个方法逻辑执行是否成功,有两种方式,一种是抛出异常,一种是提供 boolean 类型 的返回值。举一个例子,用户登录,某些人将 login 方法的返回值定义为 int,然后枚举出各 个值的含义,比如 0 代表成功,1 代表用户名不存在等等;而有些人,把这些枚举值看成是 use case 中的异常流,将它们定义为异常对象,遇到“异常”情况直接抛出异常从而实现分 支的流程。第一种方式是典型的 C 语言面向过程风格,第二种方式,带有强烈的面向对象味 道,特别是 java 提供了 checked Exception,貌似偏离主题了。 java 中很多方法的执行成功依赖于异常的分支实现,但也有提供返回值的实现,比如这里的 File.delete 方法,上面的写法忽略了返回值(如果调用某个方法却不使用其返回值要特别注 意),删除一个文件很可能不成功,但是从代码里并没有看到这一层面的意思。 解决方法: 文件删除不成功该怎么办?现在能处理就处理,现在不能处理就把父类的方法也改成有返回 值的,然后向上传递,这跟处理异常的道理是一样的,当然,你也可以把它封装成一个异常 对象。 48. RV_RETURN_VALUE_IGNORED Bug: BackupFileListPanel$PopupListener.maybeShowPopup(MouseEvent) ignores return value of String.trim() Pattern id: RV_RETURN_VALUE_IGNORED, type: RV, category: CORRECTNESS 解释: String 是一个不可变类,调用 String 上的任何操作都会返回新的 String 对象,虽然 String 是 一个 class,但实际上对它的任何操作都可以把它看成基本数据类型,比如 s.trim 方法是不会 改变 s 值的。 解决方法: S = s.trim 49. DM_BOOLEAN_CTOR 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 对象。 50. RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE Pattern id: RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE, 解释: StringBuffer 连接更有効率,因此,建议使用 StringBuffer 51. DM_NUMBER_CTOR new Integer(int) 和 Integer.valueOf(int) bug 描述: [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. 说明: [参考]http://www.cnblogs.com/hyddd/articles/1391318.html 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。
还剩35页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 10 金币 [ 分享pdf获得金币 ] 0 人已下载

下载pdf

pdf贡献者

blue_kite

贡献于2012-11-16

下载需要 10 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf