SonarLint介绍
关于SonarLint的介绍可以看一下这篇文章——Sonarlint插件详解
本文主要记录一些在开发中常见的SonarLint的issue。首先介绍一下issue的等级和状态。
SonarLint的Issue等级&状态
- 等级
BLOCKER
(致命):会影响应用程序的缺陷:内存泄漏,未关闭的JDBC连接…必须立刻修复的代码CRITICAL
(关键 ):可能会影响应用程序的缺陷或者是安全性缺陷:空的catch块,sql注入,…必须立刻查看代码MAJOR
(主要):可能会影响开发者效率的质量缺陷:未覆盖的代码,重复块,未使用的参数….MINOR
(微小):可能会影响开发者效率的质量缺陷:每行不能太长,“switch”语句应该至少有三个条件,….INFO
(未知):既不是缺陷也不是质量问题,只是一个发现。- 状态
Bug
:窃听器(可靠性),避免错误和未定义的行为Vulnerability
:漏洞(安全),避免破坏或攻击入,…必须立刻查看代码Code Smell
:代码嗅觉(可维护性),简化代码更新,并提高开发人员的速度
工具介绍
开发工具: IDEA
扫描工具: IDEA的SonarLint插件
开发语言: Java
开发环境: JDK1.8
SonarLint相关Issue(持续更新...)
-
BLOCKER
-
CRITICAL
-
Cognitive Complexity of methods should not be too high
解释: 方法的认知复杂度不应该太高
状态: Code Smell
案例: 略(列出来了代码太多了)
解读: 这个问题指的是一个方法体里面的逻辑太复杂了,你写的方法里每一个if,else,for都会统计一次,每个方法的统计值不能超过15
解决方法:把里面判断或者循环进行抽象,变成一个独立方法再进行调用即可
-
"String#replace" should be preferred to "String#replaceAll"
解释: “String#replace”应该优先于“String#replaceAll”,字符串的replace()替换方法应该优先于replaceAll()方法
状态: Code smell
案例: 略
解读: replaceAll()底层实现会调用匹配方法,判断第一个参数是不是正则表达式,如果不是,replace()和replaceAll()所做的事情完全相同
解决方法: 将replaceAll()方法替换成为replace()
-
String literals should not be duplicated
解释: 字符串文字不应重复
状态: Code smell
案例:
Map<String,Object> map = HashMap(); if(0 == code){ map.put("str",object1); } else if(1 == code){ map.put("str",object2); } else { map.put("str",object3); }
解读: 一个方法内,相同的字符串被重复使用而没有定义变量的时候,就会提示该问题,因为相同的字符串直接使用,没有定义变量的话,重构代码时候可能会出现更新遗漏
解决方法: 将重复的字符串定义为一个变量
Map<String,Object> map = HashMap<>(); String key = "str"; if(0 == code){ map.put(key,object1); } else if(1 == code){ map.put(key,object2); } else { map.put(key,object3); }
-
"static" base class members should not be accessed via derived types
解释: 不应通过派生类型访问“静态”基类成员
状态: Code smell
案例:
PageInfo<updateRecord> pageInfo = PageHelper.startPage(request.getPageNum(), request.getPageSize()).doSelectPageInfo(() -> updateRecordMapper.selectByExample(updateRecordExample));
解读: 这里的PageHelper是一个派生类,不应该通过这个类去访问基类(PageMethod)的方法
解决方法: 使用基类访问基类的方法
PageInfo<updateRecord> pageInfo = PageMethod.startPage(request.getPageNum(), request.getPageSize()).doSelectPageInfo(() -> updateRecordMapper.selectByExample(updateRecordExample));
-
-
MAJOR
-
Null pointers should not be dereferenced
解释: 被引用的值不应该为空
状态: Bug
案例:
Map<String, Object> map = new Hash<>(8); Group ruleGroup = groupMapper.selectByPrimaryKey(groupId); map.put("groupId", groupId); map.put("groupName",[1]null != ruleGroup ? ruleGroup.getGroupName() : ""); map.put("groupCreateTime", [2]ruleGroup.getCreateTime());
解读: 这个Group对象是从数据库中查询出来的,可能为空
解决方法: 可以先设置初始值,再进行判空赋值
Map<String, Object> map = new Hash<>(8); Group ruleGroup = groupMapper.selectByPrimaryKey(groupId); String groupName = ""; Date groupCreateTime = null; if (null != ruleGroup) { groupName = ruleGroup.getGroupName(); groupCreateTime = ruleGroup.getCreateTime(); } map.put("groupName", groupName); map.put("groupCreateTime", groupCreateTime);
-
Assignments should not be made from within sub-expressions
解释:不应从子表达式中进行赋值
状态: Code smell
案例:
boolean flag; if ([1]flag = !this.formalCheckRequirement(totalValues, target)) { ... }
解读: [1]处创建对象flag之后,在子表达式中赋值,导致提示该问题
解决方法: 创建值的时候初始化,避免在子表达式中进行赋值
boolean flag = !this.formalCheckRequirement(totalValues, target); if (flag) { ... }
-
Unused "private" methods should be removed
解读: 未使用的私有方法应该移除
状态: Code smell
案例: 略(这个没啥举例的,代码里面有没有用到的private修饰的方法删除即可)
解读: 从不执行的私有方法是死代码,应该删除的不必要的、无效的代码
解决方法: 将代码中没有使用到的private方法删除即可
-
Sections of code should not be commented out
解读: 不应注释掉代码段
状态: Code smell
案例: 略(这个没啥举例的,代码里面有注释的代码段检查是否会使用,不使用的话直接删除)
解读: 不应该注释代码段,会是代码膨胀降低可读性。应该删除未使用的代码,有需要的话可以从源代码控制记录中检索
解决方法: 将注释的代码段删除或者重新使用
-
"java.nio.Files#delete" should be preferred
解释:“java.nio.Files#delete”应该是首选(相比于File.delete())
状态: Code smell
案例:
File resumeZipFile = File.createTempFile("prefix","suffix"); [1]resumeZipFile.delete();
解读: [1]处需要删除一个创建的临时文件,使用java.io.File#delete删除,但是不需要其他的操作,这个时候应该优先使用java.nio.Files#delete
解决方法: 使用java.nio.Files.delete()
File resumeZipFile = File.createTempFile("prefix","suffix"); Files.delete(resumeZipFile.toPath());
-
Collapsible "if" statements should be merged
解释:应合并可折叠的“if”语句
状态: Code smell
案例:
if(null != object) { if(1 == object.code) { ... } }
解读: 可以折叠的if语句应该合并在一起,这样能够提高代码可读性
解决方法:
if(null != object && 1 == object.code) { ... }
-
Synchronized classes Vector, Hashtable, Stack and StringBuffer should not be used
解释:不应使用同步类 Vector、Hashtable、Stack 和 StringBuffer
状态: Code smell
案例:
Stack<String> resultStack = new Stack<>(); Vector<String> vector = new Vector<>(); Hashtable<String,Object> hashtable = new Hashtable(); StringBuffer stringBuilder = new StringBuffer();
解读: 当你在代码中使用Stack这些同步类的时候就会提示该问题,因为使用同步类会导致性能下降
解决方法: 可以使用相关的不同步类进行替换
Deque<String> resultStack = new Deque<>(); ArrayList<String> vector = new ArrayList<>(); HashMap<String,Object> hashtable = new HashMap(); StringBuilder stringBuilder = new StringBuilder();
-
Methods should not have too many parameters
解释:方法不应该有太多参数
状态: Code smell
案例:
public void doSomething(int param1, int param2, int param3, String param4, long param5) { ... }
解读: 一个方法不应该有太多的参数,如果必须要这么多参数的话,可以使用一个结构包装参数
解决方法:
public Class Param{ int param1, int param2, int param3, int param4, int param5, } public void doSomething(Param param) { ... }
-
-
MINOR
-
Math operands should be cast before assignment
解释: 数学操作数应在赋值之前强制转换
状态:Bug
案例:
int addNum = 5; LocalDate localStartDate = LocalDate.parse("2022-02-01"); localStartDate.plusDays([1]addNum + 1);
解读:localStartDate.plusDays()方法参数要求是long类型,而[1]处直接将表达式作为参数传递,可能会引起类型转换错误
解决方法: 提前进行类型转换
int addNum = 5; long addDayNum = (long) addNum + 1 LocalDate localStartDate = LocalDate.parse("2022-02-01"); localStartDate.plusDays(addDayNum);
-
Boxed "Boolean" should be avoided in boolean expressions
解释: 布尔表达式中应避免使用封装类型的“布尔”
状态: Code smell
案例:
public Boolean judgment() {...} if([1]judgment()){...}
解读: [1]处调用方法作为判断条件,获取到的返回值是Boolean,Boolean可能为null,有可能导致NPE异常
解决方法: 使用equals进行比较,避免NEP异常
if(Boolean.FALSE.equals(judgmentDate(startDate, endDate))){...}
-
Collection.isEmpty() should be used to test for emptiness
解释: 应该使用Collection.isEmpty()来判断是否为空
状态: Code smell
案例:
List<String> list = new ArrayList<>(8); if([1]s1.size() != 0) {...}
解读: [1]处应该使用Collection.isEmpty()来进行判空,不应该使用size != 0,Sonarlint给的解释是因为使用Collection.isEmpty()可读性更好和性能更高
解决方法:
List<String> list = new ArrayList<>(8); if(!s1.isEmpty) {...}
-
Local variable and method parameter names should comply with a naming convention
解释: 局部变量和方法参数名称应符合命名约定
状态: Code smell
案例: 略(这个就是命名不规范引起的,只要修改命名就行了)
解读: 无
解决方法: 按照规范命名
-
Declarations should use Java collection interfaces such as "List" rather than specific implementation classes such as "LinkedList"
解释: 声明应该使用诸如“List”之类的Java集合接口而不是诸如“LinkedList”之类的特定实现类
状态: Code smell
案例:
public HashMap<String, String> get(){...}
解读: 这个方法的返回类型是HashMap,是Map的实现类,应该使用Map
解决方法:
public Map<String, String> get(){...}
-
Return of boolean expressions should not be wrapped into an "if-then-else" statement
解释: 布尔表达式的返回不应包含在“if-then-else”语句中
状态: Code smell
案例:
if (option.equals(TargetEnum.Option.LESS_EQUAL.getCode())) { return true; }
解读: 判断表达式之后返回boolean值,有点类似于脱裤子放屁了,当然这里这是只是举例
解决方法:
return option.equals(TargetEnum.Option.LESS_EQUAL.getCode());
-
Loops should not contain more than a single "break" or "continue" statement
解释:循环不应包含多个“break”或“continue”语句
状态: Code smell
案例:
for (int i = 1; i <= 10; i++) { // Noncompliant - 2 continue - one might be tempted to add some logic in between if (i % 2 == 0) { continue; } if (i % 3 == 0) { continue; } System.out.println("i = " + i); }
解读: 将多个continue合并为一处
解决方法:
for (int i = 1; i <= 10; i++) { // Noncompliant - 2 continue - one might be tempted to add some logic in between if (i % 2 == 0 || i % 3 == 0) { continue; } System.out.println("i = " + i); }
-
Lambdas should be replaced with method references
解释:应将 Lambda 替换为方法引用
状态: Code smell
案例:
List<GroupEmployee> groupEmployees = new ArrayList(); employeeIds = groupEmployees.stream().map(groupEmployee -> groupEmployee.getCompanyEmployeeId()).collect(Collectors.toList());
解读: 将多个continue合并为一处
解决方法: 使用::(方法引用)替换->(Lambda)
List<GroupEmployee> groupEmployees = new ArrayList(); employeeIds = groupEmployees.stream().map(groupEmployee::groupEmployee.getCompanyEmployeeId()).collect(Collectors.toList());
-
-
INFO
-
Track uses of "TODO" tags
解释: 跟踪“TODO”标签的使用
状态: Code smell
案例:
void doSomething{ //TODO }
解读: 当代码中有TODO标签的时候,就会有这个规则的出现,做一个提示作用
解决方法: 实现相关代码,去掉TODO
-