1. 空指针问题
a. 空指针隐患(调用对象的属性和方法是需要对对象进行判空。)
public class FindBugsDemo { public String wrong(List<String> list) { // 错误 if (list.isEmpty()) { return "list is empty"; } else { return "list's size is " + list.size(); } } // 正确 public String right(List<String> list) { if (list == null || list.isEmpty()) { return "list is empty"; } else { return "list's size is " + list.size(); } } }b. 参数调用之前需要判断非空
// 错误 public int wrong(List<String> list) { return list.size(); } // 正确 public int right(List<String> list) { return (list == null) ?0:list.size(); }
// 错误 List list = getAlist(); for(String s:list ){ … }
2. 资源未释放(类似IO CON 等系统资源使用完毕必须确保关闭!建议在finally中关闭)
public void demo(String path) { InputStream in = null; try { in = new FileInputStream(path); } catch (Exception e) { logger.error(e.getMessage(), e); } finally { IOUtils.closeQuietly(in); } }
3. equals误用
a. 两个不同类型的对象调用equals方法,如果equals方法没有重写,则==比较的结果永远是false
String a = "hello"; String b = "hello"; boolean result = a.equals(b); public void test(str){ if(str==null||str==””)//String用equals比较 }
b. 比较时两个比较的类型不一致
数组对象使用equals方法和非数组对象进行比较。即使比较的双方都是数组对象也不应该使用equals方法,而应该比较它们的内容是否相等使用java.util.Arrays.equals(Object[], Object[]);
(注:禁止无意义的equals(null)比较,Obj.equals(null)比较操作 毫无意义,请确保比较两个对象都不会为null)
4. 遗漏负数的情况
// 错误 public boolean isOdd2(int a) { return (a%2) == 1; } // 正确 public boolean isOdd3(int a) { return (a%2) != 0;//或者判断下a是否>0 或abs }
5. DateFomat不是线程安全的(DateFomat不是线程安全的,因此不要定义为静态成员)
private static final DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); // 错误,线程不安全 public String formatYyyyMMdd(Date date) { return format.format(date); } // 正确 public String format(Date date) { DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); return format.format(date); }
6.永远返回true的instanceof(这个instanceof将永远返回true(除非对象的值为null),这样写让人不易理解你的目的)。
7. 类实现了序列化接口,而他父类没有对应的可访问的无参构造函数
Serializable接口,而对应的父类没有实现. 当反序列化时,超类中的属性需要通过调用其无参构造函数来实现初始化.如果超类中不存在,在执行序列化和反序列化时会发生异常.
8. 参数类型不匹配(方法参数的泛型类型不匹配)
List<String>中add方法,泛型类型为String,却传入一个Foo类型的对象 List list1 = new ArrayList(); list1.add(Integer.valueOf(12)); setRecords(list1); public void setRecords(List<String> records) { … }
9. 类型转换异常
存在发生castException的隐患,请检改行转换语法是否正确,有必要的话加上instanceof 判断。
一般有3种情况:
1.无公共基类类型进行显示类型转换
2.纯基类型向派生类显示类型转换
3.继承链中非公共节点间进行显示类型转换
(注:集合类在向array转换时显示声明要转换的数组类型,不要采用强转方式进行,容易引发类型转换异常)
List<String> keywordRelCatIds = null; String[] array = keywordRelCatIds.toArray(new String[keywordRelCatIds.size()]); //显示说明事转成string数组,而不是用(string[])强转方式来写
10.线上代码禁止调用e.printStackTrace!
11.线上代码禁止调用SYSTEM的某些方法
禁止system.println、 system.err 、system.gc 在线上代码中出现,可以在自己线下做测试的时候搞上线的代码不要包含测试用的的代码
12. Log使用不规范
日志在info,warn,debug输出时需要日志级别判断,示例如下:
if (logger.isInfoEnabled()) { logger.info("XXX", e); }
另建议
1.在本类中log声明时用 final static修饰,提高性能!
2.采用SLF4j作为日志输出,提高性能!
13. finally语句块中禁止return
static boolean f() { try { return true; } finally { return false; } }
在这个应用场景中,不要用return来退出finally语句块,因为finally语句块都会被执行到,这样try程序块中执行正常也会在finally中退出,不会再回到try程序块中。
Try-catch的流程说明:
try { … return true; }catch(异常){ … } finally { … }
1,try-catch-finally,
如果try语句块遇到异常,try下面的代码就不执行了,转而执行catch语句块,执行完再执行finally语句块,最后结束。
2,try-finally,
如果在try语句块中执行到return语句前一条,未遇到异常,转而执行finally语句块,执行完再执行try中的return语句。不要用return、break、continue或throw来退出finally语句块
14. BigDecimal使用不规范
double RATE = 0.97; System.out.println("===fail====>"+new BigDecimal(RATE)); System.out.println("==succ===>"+new BigDecimal(String.valueOf(RATE))); 不准确输出:===fail====>0.969999999999999973658203125 正确输出:===succ===>0.97 如果后续代码针对new BigDecimal(RATE)进行操作存在计算精准度问题!15. 字段锁误用(用字段来作为同期的锁的时候,是对这个字段参照的对象加锁。当字段的值变化的时候,不同的线程所参照的字段的值不一定相同。这种情况下,无法防止对字段的同时更新)
private List<String> records = new ArrayList<String>(); public void setRecords(List<String> records) { synchronized(this.records){ this.records = records; //对records锁定,但是值又被改变,这样锁毫无意义。 } } //建议new Byte 出来然后锁这个永不改变的byte
16. Array.asList得到的集合不允许增加和删除元素(Array.asList得到的集合是一个unmodifiedList,对其内容操作是无效的,会抛出异常)
List<String> mylist = Arrays.asList(strArr); mylist.add("some string");//这里不允许给集合加入元素 mylist.remove(0);//这里不允许删除集合类的元素 Arrays.asList得到的集合不允许增加和删除元素!
17. List.subList得到的新集合后,不允许对原集合做结构性更改!
List<String> list2 = list1.subList(0, 2); list1.add("e");//这里对原list1做了结构性更改 list2.get(0);//这里对list2的访问会抛出 //java.util.ConcurrentModificationException异常
18.float与double禁止比较(float和double由于精度不一致,比较时会出现期望以外的结果。)
float a = 1.0f; double b=1.0; if(a == b) { .... }
19. 遍历集合类删除元素后递归操作Bug隐患
1. 以上代码如果里层的集合中,对原集合做结构性更改会影响到外层集合的遍历,外层循环时容易缺失数据。
2. 如果单纯在循环里更改原集合,要注意对原集合的更改不要混用索引和Iterator,容易报ConcurrentModificationException
private List< AliCertCategoryDO > methodA( List<AliCertCategoryDO> queryReturnList,Long parentId) { List<CertCategoryNodeDO> nodeList = new ArrayList<CertCategoryNodeDO>(); for (Iterator<AliCertCategoryDO> iter = queryReturnList.iterator(); iter.hasNext(); ) { AliCertCategoryDO aliCertCategoryDO = iter.next(); if(...){ iter.remove(); //更改了结构 List< AliCertCategoryDO > list = methodA (queryReturnList,aliCertCategoryDO.getId()); aliCertCategoryDO.setChildren(list); nodeList.add(aliCertCategoryDO); } } return nodeList; }20. 父类构造函数中调用子类中未初始化的成员( 当创建子类对象时,父类的构造函数将在子类为成员变量赋值之前触发,然而在父类的初始化方法调用子类方法时 \ 这个成员变量还没有被初始化 )
abstract class A { int hashCode; abstract Object getValue(); A() { hashCode = getValue().hashCode(); } } class B extends A { Object value; B(Object v) { this.value = v; } Object getValue() { return value; } }