findbugs常见错误总结

一、前置说明:

  官网提供了所有的bug描述信息:官网bug描述链接 ,根据findbugs扫码结果中的关键字搜索即可找到bug的描述信息甚至是修复方案。

二、findbugs常见错误

  1. Method concatenates strings using + in a loop (SBSC_USE_STRINGBUFFER_CONCATENATION)
    错误的示范如下:字符串拼接性能太差。应改用StringBuilder实例的append方法。

  2. UC_USELESS_OBJECT
    错误的示范如下:存在无效的对象。修正方式。删除无效代码即可。
    在这里插入图片描述

  3. UPM_UNCALLED_PRIVATE_METHOD
    存在从未调用的私有方法,删除无效代码即可

  4. NP_NULL_ON_SOME_PATH_EXCEPTION
    在执行的逻辑中,可能报空指针异常。见5. 下面的错误示范

  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());
}
  1. 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;
}
  1. RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
    包含对已知非空值的冗余检查。
if (StringUtils.isNotEmpty(configStr)) {
    String[] split = configStr.split(",");
    // 没必要再次检查了
    if (split != null) {
        itemTemp.setVoucherList(Arrays.asList(split));
    }
}
  1. 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()方式来进行遍历。

  1. RC_REF_COMPARISON
    引用类型的比较,应该用equals方法。
private List<FullFreeRuleItems> packageRuleDTO2Param(List<PromotionRuleDTO> ruleDTOList, Long fullFreemeteId) {
    // 省略
    for (PromotionRuleDTO ruleDTO : ruleDTOList) {
        // 不良的比较方式
        if (ruleDTO.getPromotionMetaId() == fullFreemeteId) {
           // ......
        }
    }
}
  1. 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) {

}
  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
  1. 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) {
    // 省略部分逻辑
}
  1. 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列表为空!");
    }
    // 省略部分逻辑
}
GNG
发布了118 篇原创文章 · 获赞 389 · 访问量 68万+

猜你喜欢

转载自blog.csdn.net/so_geili/article/details/90727949