代码规范问题
最近有做简单的代码规范整理,写一下规范过程使用的工具和遇见比较多的问题。
关于代码规范的重要性网上一搜一大堆,都是很有年代的一些文章,现在写这个好像有点太low了,简单列一下他们(相似的文章太多,找不到原文)所说到的几点好处:
- 有助于团队合作
- 减少BUG处理
- 降低维护成本
- 有助于代码审查
- 养成规范代码的习惯,有助于程序员自身的成长
毋庸置疑的是随着团队的发展、规范化,良好的代码规范能体现的好处会越来越明显,提升工作效率、提高代码质量等等...
为了保证代码的规范、质量,需要开发者自身养成良好的编码习惯,但是很多时候每个人的风格习惯都不一样,不利于团队协作,即使有些公司会有人工代码审查的环节,但是过于耗费人力资源,所以更多的是利用工具进行扫描分析。
代码分析包括静态分析和动态调试:
- 静态分析:指在不运行程序的情况下,对源代码进行检查分析,通过分析语法、结构、过程、接口等来检查程序的正确性,找出代码中存在的错误和缺陷。如参数不匹配、有歧义的嵌套语句、错误的递归、非法计算、可能出现的空指针引用等等。
- 动态调试:指利用调试器跟踪软件的运行,寻找程序中的漏洞。
现在主要是利用以静态分析技术实现的相关工具对代码进行检查,先列一下接触到的工具:
工具 | 分析对象 | 备注 |
Findbugs | 字节码文件 | 缺陷模式匹配、数据流分析。通过字节码分析代码存在缺陷、支持数据流分析,能查找出空指针崩溃等问题。 |
CheckStyle | Java源代码 | 缺陷模式匹配。扫描代码格式规范、代码风格的工具。 |
Alibaba java coding guidelines(基于PMD) | Java源代码 | 缺陷模式匹配、数据流分析。检测代码潜在错误。 |
- FindBugs
FindBugs是一个开源的静态代码分析工具,基于LGPL开源协议,它检查类或者JAR 文件,将字节码与一组缺陷模式进行对比以发现可能的问题。它注重检测真正的bug及潜在的性能问题 ,尤其注意了尽可能抑制误检测(false positives)的发生,不关心代码风格格式的问题。FindBugs的检测结果主要分一下几种问题类型:
-
- 正确性(Correctness):可能导致错误的代码。比如错误的强制类型转换。
- 最佳实践反例(Bad practice):违反了公认的最佳实践标准。如一些流使用后没有关闭。
- 多线程的正确性(Multithreaded correctness):关注于同步和多线程问题。如应该使用notify()而不是notifyAll()。
- 性能(Performance):潜在的性能问题。如:一个属性从没有被使用,考虑从类中去掉。
- 危险的(Dodgy):具有潜在危险的代码,可能运行期产生错误。如:对方法调用的直接引用,而方法可能返回null。
- 恶意代码漏洞(Malicious code vulnerability):可能受到恶意攻击的代码,如访问权限修饰符的定义等。
- 国际化(Internationalization):关于代码国际化相关方面的。如使用平台默认的编码格式对字符串进行大小写转换,这可能导致国际字符的转换不当。
简单列几个我们程序中FindBugs分析出现比较多的问题:
- May expose internal representation by returning reference to mutable object.
可能因使引用可指向多个对象而暴露内部存储结构。
get/set方法直接把此对象中某一属性的引用放到外部,可以随便更改,违反了封装的原则。可以在get/set方法中修改为对这类属性(引用地址)的拷贝对象做操作。但是一般不会这么做,实际使用中bean里面的属性设置后很少会再改动,这么做太麻烦了。
- Method invokes inefficient Number constructor; use static valueOf instead.
方法调用低效数构造函数,使用静态valueOf方法代替。
FindBugs推荐使用Integer.ValueOf(int)代替new Integer(int),因为这样可以提高性能。如果当你的int值介于-128~127时,Integer.ValueOf(int)的效率比Integer(int)快大约3.5倍。从源代码可以看到,ValueOf对-128~127这256个值做了缓存(IntegerCache),如果int值的范围是:-128~127,在ValueOf(int)时,他会直接返回IntegerCache的缓存。
-
Boxed value is unboxed and then immediately reboxed.
装箱的值被拆箱,然后立即重新装箱了。自动装箱拆箱的特性,只要运算中有不同类型,当涉及到类型转换时,编译器就会向下转型,再运算。
-
Switch statement found where default case is missing.
Switch没有默认情况下执行的case语句。 -
Inefficient use of keySet iterator instead of entrySet iterator.
使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历。当程序中有遍历对map的key、value操作的时候,建议使用entrySet,它的性能比keySet高。通过keySet遍历时,生成KeyIterator迭代器,如果要获取value此时还要遍历Map。而entrySet遍历生成EntryIterator 迭代器,其中包含key和value。
-
Method call passes null for non-null parameter.
对参数为null的情况没做处理。当args为空时,程序异常。
- Checkstyle
Checkstyle是一款检查Java程序源代码样式的工具。主要的检查项包括Javadoc注释、命名规范、多余没用的Imports、Size度量,如过长的方法、缺少必要的空格Whitespace、重复代码等。它可以有效的帮助我们检视代码以便更好的遵循代码编写标准,特别适用于小组开发时彼此间的样式规范和统一。
按照Sun的规范太严格了,通常需要自定义规则,使用起来很麻烦,所以后面没有使用Checkstyle。
- Alibaba java coding guidelines
Alibaba java coding guidelines是阿里巴巴2017年10月在杭州云栖大会上发布的,把《阿里巴巴 Java 开发规约》强制条目转化为自动插件,并实现了部分的自动编码。项目已经在Git上开源了,地址阿里规约插件。
阿里规约插件检查的内容参照《阿里巴巴 Java 开发规约》,基于PMD进行的代码检测,检测的问题主要分为三类,对应 Snoar 中的前三个等级,前两个级别是必须要处理的:
-
- Blocker:崩溃
- Critical:严重
- Major:重要
按照阿里规约,大量存在的问题如下,因为是中文的,而且提示信息很准确,所以修改很轻松:
-
- 方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵从驼峰形式。
- 常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
- 魔法值。
- 代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。
- 注释的双斜线与注释内容之间有且仅有一个空格。
- Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals。
- 集合初始化时,指定集合初始值大小。
- 需要进行参数校验。
- 类、类属性、类方法的注释必须使用 Javadoc 规范,使用/**内容*/格式。
其实单从这里并不能很好的做到代码规范统一,了解到正常的应该是类似Jenkins+SonarQube+Git的方式,在提交代码的时候会触发检查,不合规范的代码不允许提交。