一、前置说明:
官网提供了所有的bug描述信息:官网bug描述链接 ,根据findbugs扫码结果中的关键字搜索即可找到bug的描述信息甚至是修复方案。
二、findbugs常见错误
-
Method concatenates strings using + in a loop (SBSC_USE_STRINGBUFFER_CONCATENATION)
错误的示范如下:字符串拼接性能太差。应改用StringBuilder实例的append方法。 -
UC_USELESS_OBJECT
错误的示范如下:存在无效的对象。修正方式。删除无效代码即可。
-
UPM_UNCALLED_PRIVATE_METHOD
存在从未调用的私有方法,删除无效代码即可 -
NP_NULL_ON_SOME_PATH_EXCEPTION
在执行的逻辑中,可能报空指针异常。见5. 下面的错误示范 -
NP_NULL_ON_SOME_PATH
在某些路径上存在空指针。 下面的错误示范。若这种bug是业务需要,可以在忽略文件中配置。
@Override
public Result<Integer> addPromotion(PromotionDTO promotionDTO) {
Long id = null;
try {
log.info("promotionDTO = {}", promotionDTO);
// NP_NULL_ON_SOME_PATH
if (promotionDTO == null) {
return Result.wrapError(1, "参数异常");
}
// 省略部分代码
id = pmpPromotionConfigService.addPromotion(promotionDTO);
} catch (Exception e) {
// 省略部分代码
}
log.info("id = {}", id);
// 这里可能报异常
return Result.wrapSuccess(id.intValue());
}
- DB_DUPLICATE_BRANCHES 复制分支
使用相同的代码实现条件分支的两个分支。检查以确保这不是编码错误。
@Override
public Boolean transformDataIntoMongo(Long promotionId) {
try {
// 省略部分代码
// 状态为2 和 else中执行的逻辑相同,请检查逻辑是否有问题
if (status == 2) {
promotionMongoDao.save(promotionMongoPO);
} else {
promotionMongoDao.save(promotionMongoPO);
}
} catch (Exception e) {
logger.error(e.getMessage(), promotionId);
return false;
}
return true;
}
- RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
包含对已知非空值的冗余检查。
if (StringUtils.isNotEmpty(configStr)) {
String[] split = configStr.split(",");
// 没必要再次检查了
if (split != null) {
itemTemp.setVoucherList(Arrays.asList(split));
}
}
- WMI_WRONG_MAP_ITERATOR
错误的map迭代器
// 初始化一个map
Map<Integer, List<MarketingActivityPO>> voucherMap = packageMarketingActivityVoucher();
for (Integer key : voucherMap.keySet()) {
List<MarketingActivityVoucherPO> voucherPOList = voucherMap.get(key);
// 省略部分代码
}
KeySet():
将Map中所有的键存入到set集合中。因为set具备迭代器。所有可以迭代方式取出所有的键,再根据get方法。获取每一个键对应的值。 keySet():迭代后只能通过get()取key
entrySet():
Set<Map.Entry<K,V>> entrySet() //返回此映射中包含的映射关系的 Set 视图。 Map.Entry表示映射关系。entrySet():迭代后可以e.getKey(),e.getValue()取key和value。返回的是Entry接口 。
虽然使用keyset及entryset来进行遍历能取得相同的结果,但两者的遍历速度是有差别的。
keySet():迭代后只能通过get()取key
entrySet():迭代后可以e.getKey(),e.getValue()取key和value。
说明:keySet()的速度比entrySet()慢了很多,也就是keySet方式遍历Map的性能不如entrySet性能好
为了提高性能,以后多考虑用entrySet()方式来进行遍历。
- RC_REF_COMPARISON
引用类型的比较,应该用equals方法。
private List<FullFreeRuleItems> packageRuleDTO2Param(List<PromotionRuleDTO> ruleDTOList, Long fullFreemeteId) {
// 省略
for (PromotionRuleDTO ruleDTO : ruleDTOList) {
// 不良的比较方式
if (ruleDTO.getPromotionMetaId() == fullFreemeteId) {
// ......
}
}
}
- UCF_USELESS_CONTROL_FLOW
包含一个无用的控制流语句,在该语句中,无论是否采用分支,控制流都将继续到同一位置。比如由if语句的语句块为空导致的:
// 官网demo1,分支下无代码
if (argv.length == 0) {
// TODO: handle this case
}
// 官网demo2,空语句导致
if (argv.length == 1);
System.out.println("Hello, " + argv[0]);
// 项目中的实例
if (area.getAreaType() == 4) { // 级别定位到微仓级 城市大区都要有
getAreaType4(singlePromotion, areaCode);
} else if (area.getAreaType() == 3) { // 级别定位到城市级 大区要有 微仓为全部微仓
getAreaType3(singlePromotion, areaCode);
} else if (area.getAreaType() == 2) {
// 定位到大区级别 不管是全国还是大区,城市 微仓 全部
getAreaType(singlePromotion, areaCode);
// 空语句
} else if (area.getAreaType() == 1) {
}
- DM_BOXED_PRIMITIVE_FOR_PARSING
自动装箱再拆箱,eg如下:
String reduceStep = "123";
// 把字符串转成基本类型、然后封装成包裹类型,参与比较时又要自动拆箱
if (perReduce.intValue() >= Integer.valueOf(reduceStep))) {
// 省略部分逻辑
}
源码解释: 改为parseXXX方法较为合适。
// valueOf把字符串转成基本类型、然后封装成包裹类型。
public static Integer valueOf(String s, int radix) throws NumberFormatException {
return Integer.valueOf(parseInt(s,radix));
}
// parseXXX方法可以把字符串转成基本类型
public static int parseInt(String s, int radix) throws NumberFormatException
- RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE
某个值虽然在某处有被判断是否为null,但是这个值在该处并不可以为null,因为这个值被提前引用了,如果他有可能是null,在被引用的时候就会产生空指针异常,所以这里的判断也就是多余的,而在该判断之前的引用也可能是错误的。
- 原因
- 判断某个值是否为null的时机错误
- 解决方案
- 修改判断时机等,视具体情况而定
List<Integer> activityIdList = new ArrayList<>();
// 被重新赋值后,有可能变为null
activityIdList = activityDao.getXXX(searchDTO);
logger.info("查找到的活动个数:{}", activityIdList.size());
// 上一行已经使用过该引用,下一行再判空,为时已晚。需要更改判空的时机
if (activityIdList == null) {
// 省略部分逻辑
}
- RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT
@Override
@Transactional(rollbackFor = Exception.class)
public Result<Integer> batchCheckPromotion(List<String> idList, String userName) {
if (CollectionUtils.isEmpty(idList)) {
// 该调用没有副作用,且返回值还被忽略了
Result.wrapError(1, "传入的id列表为空!");
}
// 省略部分逻辑
}