随着业务的发展,系统会越来越庞大,原本简单稳定的功能,可能在不断迭代后复杂度上升,潜在的风险也随之暴露,导致最终服务不稳定,造成业务价值的损失。而为了减少这种情况,其中一种比较好的方式就是提高代码质量,比如通过代码审查,从而降低错误风险,但是,代码审查难度大,代码缺陷、漏洞不易发现,且审查工作随着代码量增加而增加,审查效率低。

        工欲善其事,必先利其器,因此,这篇文章给大家介绍几种检查代码质量的利器,Alibaba Java Coding Guidelines、CheckStyle、PMD、FindBugs、SonarLint,让你在关注代码质量的同时,减少 code review 的工作量,提高 code review 的效率,并通过代码质量分析去反向提升我们的代码编写能力

一、Alibaba Java Coding Guidelines

1、整体介绍:

        Alibaba Java Coding Guidelines 专注于Java代码规范,目的是让开发者更加方便、快速规范代码格式。该插件在扫描代码后,将不符合规约的代码按 Blocker、Critical、Major 三个等级显示出来,并且大部分可以自动修复,它还基于 Inspection 机制提供了实时检测功能,编写代码的同时也能快速发现问题所在。

阿里巴巴规约扫描包括:(1)OOP规约 (2)并发处理 (3)控制语句 (4)命名规约 (5)常量定义 (6)注释规范 

 2、安装步骤:

File > Settings > Plugins > Marketplace 搜索 “Alibaba Java Coding Guidelines”,按照提示进行安装,然后重启即可。

3、使用说明:

3.1、运行方式:

(1)可以Tools > 阿里编码规约 > 编码规约扫描

(2)在编辑界面或者项目区域点击右键,在右键菜单中选择“编码规约扫描”即可:

3.2、菜单功能:

(1)编码规约扫描:开始扫描代码

(2)打开/关闭实时检测功能:实时检测代码,一般机器性能比较好的话可以开启这项功能

(3)切换语言至英文:中英文切换

3.3、运行结果:

        扫描完成后显示结果如下,我们可以看到扫描结果主要分为 Blocker(阻挡者)、Critical(严重问题)、Major(主要的)三个大类,它们表示的是问题的严重程度,严重程度由高到低为:Blocker > Critical > Major,至于每一类中都会包含什么样的问题,图中的内容已经说明了一切。

        选中其中的一个问题项目,会出现如下内容(如果当前鼠标点击的是最终项,右边区域显示的是其它的内容,后面会再讲到):

 (1)指定区域搜索同一类问题:

当点击③处的按钮时,会弹出如下按钮:

这里选择扫描区域,来扫描鼠标选中的同类问题。如果按照默认选择,那么运行后的结果就如下图所示:

这里我们可以看到,显示了整个Project中的所有该类的问题。

(2)预览具体的不规范代码:

如果点击的是最终的问题点或者问题所在的类文件,那显示的就是如下界面,预览该处不规范的代码。

 3.4、工具栏功能介绍:

  • (1)Rerun Inspection: 重新运行一次扫描
  • (2)Close:关闭真个AJCG面板
  • (3)Expand All:展开结果的树状结构,整个结果是树状结构的。
  • (4)Collapse All:收起结果的树状结构
  • (5)Go Pre Problem:选择上一个问题
  • (6)Go Next Problem:选择下一个问题
  • (7)Help:帮助
  • (8)Group by Serverity:(不知道如何描述)
  • (9)Group by derectory:按目录分组/按类名分组间切换
  • (10)Filter resoled items:过滤掉已经解决的项
  • (11)Autoscroll to Source:自动滚动到源码
  • (12)Export:导出,可以导出为XML和HTML两种格式
  • (13)Edit Settings:编辑设置

二、CheckStyle:

1、整体介绍:

        CheckStyle 侧重检查编码格式和代码风格规范,如命名规范、Javadoc注释规范、空格规范、size度量(如过长的方法)、重复代码、多余Imports等,从而有效约束开发人员更好地遵循代码编写规范。Checkstyle主要是文法层面的代码编写规范的分析,对bug几乎没什么发现能力。

        Checkstyle插件中默认内置有2个执行代码检查的配置文件(Sun Checks 和 Sun Checks),但是这两个文件检查的非常详细严格,即使优秀的开源项目也会检查出来有非常多的错误告信息,所以需要导入我们自定义的配置文件。

2、安装步骤:

通过 File > Settings > Plugins > Marketplace 搜索 “CheckStyle”,按照提示进行安装,然后重启即可。

3、使用说明:

 可以看到基本都是一些缩进啥的编码规范,可以不用太关注

三、PMD:

1、整体介绍:

        PMD侧重面向安全编码规则,且具备一定的数据流分析和路径分析能力,能力比CheckStyle稍微强点,并且 PMD 支持自定义规则,PMD可以直接使用的规则包括以下内容:

  • (1)潜在的bug:空的try/catch/finally/switch语句
  • (2)未使用的代码(Dead code):未使用的变量、参数、私有方法等
  • (3)可选的代码:String/StringBuffer的滥用
  • (4)复杂的表达式:不必须的if语句、可被while替代的for循环
  • (5)重复的代码:拷贝/粘贴代码意味着拷贝/粘贴bugs
  • (6)循环体创建新对象:尽量不要在循环体内实例化新对象
  • (7)资源关闭:Connect,Result,Statement等使用之后确保关闭掉

2、安装步骤:

        通过 File > Settings > Plugins > Marketplace 搜索 “PMDPlugin”,按照提示进行安装,然后重启即可

3、使用说明:

参考文章:http://wjhsh.net/andy-songwei-p-11830812.html

3.1、运行方式:

(1)从Tools菜单中启动:

通过 Tools > Run PMD 可以看到如下的界面,如果通过该方式启动,扫描的范围就是整个项目中的文件了。

  • Pre Defined:预定义的规则,也就是插件自带的检测规则。后面展开的列表中列出了所有的规则列表,想扫描哪一种类型的问题,点击即可。其中“All”表示使用所有的规则。
  • Custom Rules:自定义的检测规则,PMD允许用户根据需要自定义检查规则,默认这里是不可点击的,需要在设置中导入自定义规则文件后方可选择。

(2)从右键菜单中启动:

        在文件或者编辑器中点击右键,也可以看到“Run PMD”选项,如果通过该方式启动, 检测范围取决于鼠标或光标当前所选中的区域。

3.2、运行结果:

        运行后会出现如上所示的面板,左边工具栏,鼠标停留在上面会提示其功能;右边显示了检测结果,当点击具体某一问题项时,会跳转到对应的源码中。

3.3、配置检测规则:

通过 File > Settings > Other Settings > PMD 可以打开检测规则的设置界面:

         在 “RuleSets(规则设置)” 界面可以管理自定义的检测规则。因为在实际工作中,可能需要根据实际情况自定义检测规则,就可以通过这里导入,如果要使用它,需要在启动PMD进行检测时选择该自定义规则。

        点击“Options”选项卡,在其中可以配置一些检测规则选项:

        其中重点需要留意的是“Skip TestSource”这一项,因为在项目中有不少Android Studio自动生成的测试代码,如下所示,选择上述选项后可以将其过滤掉。

 

四、FindBugs:

1、整体介绍:

        FindBugs 侧重于发现代码中存在的bug,如运行时错误检测(空指针检查、未合理关闭资源、字符串相同判断错(==,而不是equals)等),它可以简单高效全面地帮助我们发现程序代码中存在的bug以及潜在隐患,针对各种问题,它提供了简单的修改意见供我们参考

2、安装步骤:

通过 File > Settings > Plugins > Marketplace 搜索 “FindBugs”,按照提示进行安装,然后重启即可

3、使用说明:

FindBugs 可以分析单个文件、包下面的所有文件、整个module下的文件、整个project下的文件,右键想要分析的文件名/包名/module名/project

分析完之后就会出现结果面板

点击对应的item在右边会定位到具体的代码,这是根据提示进行处理修改就行

4、附:常见的错误信息

4.1、Bad practice 代码坏习惯:

错误信息说明
Class names should start with an upper case letter主要包括类名的命名,以大写字母开头
Method names should start with a lower case letter方法名以小写字母开头
Field names should start with a lower case letter字段名以小写字母开头
equals()method does not check for null argumentequals()方法应该检查非空
Class defines equals() and uses Object.hashCode()一个类重写了equals方法,但没有重写hashCode方法,使用了Object对象的hashCode方法
Method ignores exceptional return value方法忽略返回值的异常信息
Equals method should not assume anything about the type of its argumentequals(Object o)方法不能对参数o的类型做任何的假设。比较此对象与指定的对象,当且仅当该参数不为 null,并且是表示与此对象相同的类型的对象时,结果才为 true。
Comparison of String objects using == or !=用==或者!=去比较String类型的对象
Method might ignore exception方法可能忽略异常
Method invokes System.exit()在方法中调用System.exit(…)语句,考虑用RuntimeException来代替
Method ignores result of InputStream.read()InputStream.read 方法忽略返回的多个字符,如果对结果没有检查就没法正确处理用户读取少量字符请求的情况

4.2、Dodgy code 糟糕的代码:

错误信息说明
Switch statement found where default case is missingSwitch没有默认情况下执行的case语句
Switch statement found where one case falls through to the next caseSwitch语句中一个分支执行后又执行了下一个分支。通常case后面要跟break 或者return语句来跳出
Dead store to local variable该指令为局部变量赋值,但在其后的没有对它做任何使用。
Write to static field from instance method在实例方法写入静态字段
Redundant nullcheck of value known to be non-null方法中对不为空的值进行为空的判断
Method uses the same code for two branches此方法使用相同的代码,以实现两个有条件的分支。检查以确保这是不是一个编码错误
Exception is caught when Exception is not thrown在try/catch块中捕获异常,但是异常没有在try语句中抛出而RuntimeException又没有明确的被捕获
Integral division result cast to double or float整形数除法强制转换为double或者float类型
Possible null pointer dereference due to return value of called method方法的返回值没有进行是否为空的检查就重新赋值,这样可能会出现空指针异常
Useless object created对象创建了,但并没有使用
Unread public/protected field没有用到的字段

4.3、Internationalization 代码国际化相关:

错误信息说明
Consider using Locale parameterized version of invoked method使用平台默认的编码格式对字符串进行大小写转换,这可能导致国际字符的转换不当。使用以下方式对字符进行转换

4.4、Performance 代码性能相关:

错误信息说明
Boxing/unboxing to parse a primitive类型转换,比如字符串转换成int,应该使用 Integer.parseInt(“”) 代替 Integer.valueOf(“”)
Method concatenates string using + in aloop每次循环里的字符串+连接,都会新产生一个string对象,在java中,新建一个对象的代价是很昂贵的,特别是在循环语句中,效率较低。解决办法:使用StringBuffer或者StringBuilder重用对象
Private method is never called私有方法没有被调用
Explicit garbage collection;extremely dubious except in benchmarking code在代码中显式的调用垃圾回收方法,这样做并不能起作用。关闭操作或者finalize方法中调用垃圾回收方法会导致很多的性能浪费,这样大规模回收对象时会造成处理器运行缓慢。
Unread field:should this field be static?没有用到的static 字段
should be a static inner class此内部类应该使用static修饰

4.5、Experimental

错误信息说明
Method may fail to clean up stream or resource on checked exception这种方法可能无法清除(关闭,处置)一个流,数据库对象,或其他资源需要一个明确的清理行动,解决方法:流的关闭都写在finally里面

4.6、Malicious code vulnerability 恶意破坏代码相关:

错误信息说明
May expose internal representation by incorporating reference to mutable object此代码把外部可变对象引用存储到对象的内部表示。如果实例受到不信任的代码的访问和没有检查变化,将危及对象和重要属性的安全。解决方式:存储一个对象的副本,在很多情况下是更好的办法。
Field isn’t final but should be此字段前应该加final
Field isn’t final and can’t be protected from malicious code一个静态字段是可以被恶意代码或其他的包访问修改。可以把这种类型的字段声明为final类型的以防止这种错误
Field should be package protected此字段前应该加protected

4.7、Multithreaded correctness 多线程代码正确性相关:

错误信息说明
Static DateFormatDateFormat 在多线程中本身就是不安全的,如果在线程范围中共享一个DateFormat的实例,而不使用一个同步的方法在应用中就会出现一些奇怪的行为
Call to static DateFormat DateFormats多线程使用本就是不安全的,改进方法:需要创建多实例或线程同步

4.8、Correctness 代码正确性相关:

错误信息说明
Nullcheck of value previously dereferenced此代码之前没有进行null值检查,解决办法:进行null检查
Possible null pointer dereference可能为null
Null pointer dereference对象赋为null值后,没有被重新赋值
Possible null pointer dereference in method on exception path在异常null值处理分支调用的方法上,可能存在对象去除引用操作
value is null and guaranteed to be dereferenced on exception path exception分支上,存在引用一个null对象的方法,引发空指针异常
Self comparison of value with itself方法中对一个局部变量自身进行比较运算,并可说明错误或逻辑错误。请确保您是比较正确的事情
An apparent infinite recursive loop明显的无限迭代循环,将导致堆栈溢出

五、SonarLint:

1、整体介绍:

        sonar 比 Findbugs 高了一个层级,它不仅关注常规静态BUG,还关注到了如代码质量、包与包、类与类之间的依赖情况,代码耦合情况,类、方法、文件的复杂度,代码中是否包含大量复制粘贴的代码,关注的是项目代码整体的健康情况。sonar 有两种使用方式:插件和客户端,sonar 的插件名称为 sonarLint。

2、安装步骤:

通过 File > Settings > Plugins > Marketplace 搜索 “SonarLint”,按照提示进行安装,然后重启即可

3、使用说明:

        右键项目或者文件进行如上图所示操作,执行之后可以看到如下信息,如果代码中有不合理的地方会在report中显示,同时点击错误的地方在右边会给出建议的修改供参考。

 4、配置 SonarLint 服务端:

4.1、配置 Sonar 服务器:

        sonarLint 插件的使用场景是自用自审,但 sonar 也提供了平台版本,使用场景则是他审,sonar 平台的搭建就不在这篇文章介绍了,感兴趣的读者可以自己上网查看,我们这里主要介绍如何在 sonarLint 插件中配置关联 sonar 平台服务器的工程,进行本地检查:

        点击新增按钮,输入Configuration Name,配置sonarlint 服务器的地址,然后下拉框选择 Login/Password,输入 sonarlint服务器的账号密码

 4.2、具体 Sonar工程配置:

        配置完服务器之后,需要针对具体工程进行配置,点击 connection下拉框,选择上面配置好的服务器连接,然后点击 Search in list,找到对应的工程:

 4.3、使用 SonarLint 检查:

        配置完上面两步之后,接下来就可以选择要进行检查的类或者目录进行 sonarlint 检查了(跟第3点的使用方式一致),同时,在 commit 代码的时候,勾选 “Perform Sonarlint analysis”,会针对你要提交的代码进行sonarlint检查

 

总结:

(1)检查代码规范的话,直接使用 Alibaba Java Coding Guidelines 就可以了

(2)找 BUG 的话,使用 PMD、Findbugs、SonarLint 相互补充:

  • PMD 自定义能力强,用来自定义项目BUG规则非常好用

  • Findbugs 找 BUG 能力很强,我们拿找到的BUG给新员工培训也很好。

  • SonarLint 规则丰富,比 Findbugs 能覆盖到更全的场景

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐