首页 > 技术文章 > Sonar代码扫描常见问题

Uni-Hoang 2021-10-18 16:30 原文

Use try-with-resources or close this "BufferedOutputStream" in a "finally" clause.

参考:https://blog.csdn.net/libusi001/article/details/103848922

解决方法: 在finally中关闭FileInputStream,主要是关闭方式不对,finally代码块中,应该要对每个stream进行单独关闭,而不能统一写在一个try-catch代码中。

A NullPointerException might be thrown as ‘XXX’ is nullable here.

空指针,解决方式:先判断或者先实例化,再访问里面的属性或者成员。

Use a “double” or “BigDecimal” instead.

说明:两个float计算会产生不精确的结果
解决方案:将float换为double或者BigDecimal计算
举例:
float a = 16777216.0f;
float b = 1.0f;
float c = a + b; // Noncompliant; yields 1.6777216E7 not 1.6777217E7
double d = a + b; // Noncompliant; addition is still between 2 floats

换为
float a = 16777216.0f;
float b = 1.0f;
BigDecimal c = BigDecimal.valueOf(a).add(BigDecimal.valueOf(b));
double d = (double)a + (double)b;

Cast one of the operands of this multiplication operation to a “long”

说明:int数运算最终再把结果转为long将有可能产生溢出
解决方案:转换为long型预算

举例:
long bigNum = Integer.MAX_VALUE + 2; // Noncompliant. Yields -2147483647

换为
long bigNum = Integer.MAX_VALUE + 2L;

Close this “XXX”.

说明:流没有显示的关闭。
解决方案:在fianlly语句块内关闭。

Remove or correct this useless self-assignment.

说明:缺少this
解决方案:用this.xxx=xxx来替代
举例:
public void setName(String name) {
name = name;
}

换为
public void setName(String name) {
this.name = name;
}

Correct this “&” to “&&”.

说明:错误的使用&和&&
解决方案:根据情况使用&和&&

This branch can not be reached because the condition duplicates a previous condition in the same sequence of “if/else if” statements

说明:if else if 语句判断条件重复
解决方案:去掉多余的判断条件

Make this “XXX” field final.

说明:将某个字段置为final,常见在Exception的参数
解决方案:将字段置为final

Remove this call to “equals”; comparisons between unrelated types always return false.

说明:去掉equals判断语句,因为总为false
解决方案:去掉equals语句

Remove this return statement from this finally block.

说明:在finally语句块中有return语句
解决方案:去掉finally语句块的return语句或者放在finally语句块之外

Remove this continue statement from this finally block.

说明:在finally语句块中有continue语句
解决方案:去掉finally语句块中的continue语句或者放在finally语句块之外

Equality tests should not be made with floating point values.

说明:浮点数之间用 == 来比较大小不准确
解决方案:用Number或者BigDecimal来比较

举例:
float myNumber = 3.146;
if ( myNumber == 3.146f ) { //Noncompliant. Because of floating point imprecision, this will be false
// …
}
if ( myNumber != 3.146f ) { //Noncompliant. Because of floating point imprecision, this will be true
// …
}

Add a type test to this method.

说明:强转前未判断类型
解决方案:强转前先判断类型

举例:
ErpVO ev = (ErpVO) obj;
return this.userCode.equals(ev.getUserCode());

换为
if (obj == null) {
return false;

}

if (obj.getClass() != this.getClass()) {
return false;

}

Add an end condition to this loop.

说明:没有退出条件

解决方案:根据情况来决定方法的退出条件

Make “XXX” an instance variable.

说明:有些类不是线程安全的,将变量生命为静态的可能会导致线程安全问题
解决方案:将变量声明为实例的。

Strings and Boxed types should be compared using "equals()"

字符串和包装类型对比时应该使用equals方法。

解释
使用引用相等==或!=比较java.lang.String或包装类型(如java.lang.Integer)的两个实例几乎总是false,因为它不是在比较实际值,而是在内存中的位置(地址)。

在Java 中包装类型与基本数据类型存储位置不同。

  • Java 基本数据类型存放位置
    方法参数、局部变量存放在栈内存中的栈桢中的局部变量表
    常量存放在常量池中

  • 包装类型如Integer存放位置
    常量池
    堆内存

Integer 存储在常量池中时可以使用对比,但当在堆内存中时,使用对比,实际对比的是两个内存地址而非值。

Reduce the number of conditional operators (4) used in the expression (maximum allowed 3)

在一个表达式中条件判断不应该超过三个

//修改前
boolean rollback = (receiveType == Constants.ONE || receiveType == Constants.TWO || receiveType == Constants.THREE) && (result == null || !result.isSuccess());

//修改后,提取成一个方法
boolean rollback = checkReceiveType(receiveType) && (result == null || !result.isSuccess());

private boolean checkReceiveType(int receiveType) {
        return receiveType == Constants.ONE || receiveType == Constants.TWO || receiveType == Constants.THREE;
    }

The Cyclomatic Complexity of this method "xxx" is 19 which is greater than 15 authorized.

代码的复杂度太高了,通常可以把代码中独立的业务逻辑抽取成单独的方法;
sonar扫描是按照一个条件判断算一个复杂度的,也就是说在一个方法中不要写超过15个条件判断;
灵活使用StringUtils.isAnyBlank/isNoneBlank/BooleanUtils.and/BooleanUtils.or可以把多个条件判断变成一个

//修改前
if(this.acId== null||this.userId==null||!Md5Util.encrypt(inputSign).equals(this.getMd5Sign())){
                flag = false;
            }
//修改后
if(StringUtils.isAnyBlank(this.acId,this.userId)||!Md5Util.encrypt(inputSign).equals(this.getMd5Sign())){
                flag = false;
            }

Return an empty collection instead of null

不能直接返回null

//修改前
return null

//修改后
return Collections.emptyList()/Collections.EmptyMap/Collections.EmptySet/new String[0]

Refactor this code to not nest more than 5 if/for/while/switch/try statements.

if/for/while/switch/try嵌套不能超过5层

Iterate over the "entrySet" instead of the "keySet"

遍历map时,使用entrySet,而不是keySet;

解释
"entrySet()" should be iterated when both the key and value are needed;
通过查看源代码发现,调用方法keySetMap.keySet()会生成KeyIterator迭代器,其next方法只返回其key值,而调用entrySetMap.entrySet()方法会生成EntryIterator 迭代器,其next方法返回一个Entry对象的一个实例,其中包含key和value。
所以当我们用到key和value时,用entrySet迭代性能更高

Use "Integer.parseInt" for this string-to-int conversion

使用Integer.parseInt 代替 Integer.valueOf

解释
Integer.parseInt(s)是把字符串解析成int基本类型,
Integer.valueOf(s)是把字符串解析成Integer对象类型

Replace "Collections.EMPTY_LIST" by "Collections.emptyList()

Collections.EMPTY_LIST VS Collections.emptyList()
Collections. emptyList()返回的也是一个空的List,它与Collections.EMPTY_LIST的唯一区别是,Collections. emptyList()支持泛型,所以在需要泛型的时候,可以使用Collections. emptyList()。
注意,这个空的集合是不能调用add来添加元素的,会直接抛异常

为啥不直接new ArrayList()呢?
因为new ArrayList()在初始化时会占用一定的资源

————————————————
参考:
https://blog.csdn.net/asdasd3418/article/details/83791630/
https://www.jianshu.com/p/f67a6f18e066

推荐阅读