目录:
- 规约
- 参考
1、命名规约
1.1、【强制】禁止使用非标准的英文缩写
1 | 反例: AbstractClass 缩写成 AbsClass;condition 缩写成 condi。 |
1.2、 【强制】命名禁止出现 “_”
1 | 反例: _name 、 name_ 、i_name |
1.3、 【推荐】包名全部小写。点分隔符之间尽量只有一个英语单词,即使有多个单词也不使用下划线或大小写分隔
1 | 正例: com.vip.javatool |
1.4、 【强制】类名与接口名使用UpperCamelCase风格,遵从驼峰形式
Tcp, Xml等缩写也遵循驼峰形式,可约定例外如:DTO/ VO等
1 | 正例:UserId / XmlService / TcpUdpDeal / UserVO |
1.5、 【强制】方法名、参数名、成员变量、局部变量使用lowerCamelCase风格,遵从驼峰形式
1 | 正例: localValue / getHttpMessage(); |
1.6. 【强制】常量命名全大写,单词间用下划线隔开。力求语义表达完整清楚,不要嫌名字长
1 | 正例: MAX_STOCK_COUNT |
例外:当一个static final字段不是一个真正常量,比如不是基本类型时,不需要使用大写命名。
1 | private static final Logger logger = Logger.getLogger(MyClass.class); |
1.7. 【推荐】枚举类名以Enum结尾; 抽象类使用Abstract或Base开头;异常类使用Exception结尾;测试类以它要测试的类名开始,以Test结尾
1 | 正例:DealStatusEnum, AbstractView,BaseView, TimeoutException,UserServiceTest |
1.8. 【推荐】实现类尽量用Impl的后缀与接口关联,除了形容能力的接口
1 | 正例:CacheServiceImpl 实现 CacheService接口。 |
1.9. 【强制】POJO类中布尔类型的变量名,不要加is前缀,否则部分框架解析会引起序列化错误
1 | 反例:Boolean isSuccess的成员变量,它的GET方法也是isSuccess(),部分框架在反射解析的时候,“以为”对应的成员变量名称是success,导致出错。 |
1.10、各层命名规约
A) Service/DAO 层方法命名规约
1) 获取单个对象的方法用 get 做前缀。
2) 获取多个对象的方法用 list 做前缀,复数结尾,如:listObjects。
3) 获取统计值的方法用 count 做前缀。
4) 插入的方法用 save/insert 做前缀。
5) 删除的方法用 remove/delete 做前缀。
6) 修改的方法用 update 做前缀。
B) 领域模型命名规约
1) 数据对象:xxxDO,xxx 即为数据表名。
2) 数据传输对象:xxxDTO,xxx 为业务领域相关的名称。
3) 展示对象:xxxVO,xxx 一般为网页名称。
4) POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。
2、格式规约
2.1、【强制】使用项目组统一的代码格式模板,基于IDE自动的格式化
2.2、【强制】IDE的text file encoding设置为UTF-8; IDE中文件的换行符使用Unix格式,不要使用Windows格式
2.3、【推荐】 用小括号来限定运算优先级
2.4、【推荐】 类内方法定义的顺序,不要“总是在类的最后添加新方法”
一个类就是一篇文章,想象一个阅读者的存在,合理安排方法的布局。
1)顺序依次是:构造函数 > (公有方法>保护方法>私有方法) > getter/setter方法。
如果公有方法可以分成几组,私有方法也紧跟公有方法的分组。
2)当一个类有多个构造方法,或者多个同名的重载方法,这些方法应该放置在一起。其中参数较多的方法在后面。
1 | public Foo(int a) {...} |
3)作为调用者的方法,尽量放在被调用的方法前面。
1 | public void foo() { |
2.5、【推荐】通过空行进行逻辑分段
一段代码也是一段文章,需要合理的分段而不是一口气读到尾。
不同组的变量之间,不同业务逻辑的代码行之间,插入一个空行,起逻辑分段的作用。
而联系紧密的变量之间、语句之间,则尽量不要插入空行。
1 | int width; |
2.6、【推荐】避免IDE格式化
对于一些特殊场景(如使用大量的字符串拼接成一段文字,或者想把大量的枚举值排成一列),为了避免IDE自动格式化,土办法是把注释符号//加在每一行的末尾,但这有视觉的干扰,可以使用@formatter:off和@formatter:on来包装这段代码,让IDE跳过它。
1 | // @formatter:off |
3、注释规约
3.1、【推荐】基本的注释要求
完全没有注释的大段代码对于阅读者形同天书,注释是给自己看的,即使隔很长时间,也能清晰理解当时的思路;注释也是给继任者看的,使其能够快速接替自己的工作。
代码将被大量后续维护,注释如果对阅读者有帮助,不要吝啬在注释上花费的时间。(但也综合参见规则2,3)
第一、能够准确反应设计思想和代码逻辑;第二、能够描述业务含义,使别的程序员能够迅速了解到代码背后的信息。
除了特别清晰的类,都尽量编写类级别注释,说明类的目的和使用方法。
除了特别清晰的方法,对外提供的公有方法,抽象类的方法,同样尽量清晰的描述:期待的输入,对应的输出,错误的处理和返回码,以及可能抛出的异常。
3.2、【推荐】通过更清晰的代码来避免注释
在编写注释前,考虑是否可以通过更好的命名,更清晰的代码结构,更好的函数和变量的抽取,让代码不言自明,此时不需要额外的注释。
3.3、【推荐】避免创建人,创建日期,及更新日志的注释
代码后续还会有多人多次维护,而创建人可能会离职,让我们相信源码版本控制系统对更新记录能做得更好。
3.4、【强制】代码修改的同时,注释也要进行相应的修改。尤其是参数、返回值、异常、核心逻辑等的修改
3.5、【强制】类、类的公有成员、方法的注释必须使用Javadoc规范,使用/** xxx */格式,不得使用//xxx方式
3.6、【推荐】注释不要为了英文而英文
如果没有国际化要求,中文能表达得更清晰时还是用中文。
3.7、【推荐】TODO 标记,清晰说明代办事项和处理人
正例:
1 | // TODO(who): to do what. |
反例:
1 | // TODO: refactor it |
不推荐使用 FIXME 标记。这样,在项目中搜索 TODO 就可以得到完整的 TODO list,而不需要搜索 TODO 和 FIXME。
3.8、【推荐】合理处理注释掉的代码
如果后续会恢复此段代码,在目标代码上方用///说明注释动机,而不是简单的注释掉代码。
如果很大概率不再使用,则直接删除(版本管理工具保存了历史代码)。
4、方法设计
4.1、【推荐】方法的长度度量
方法尽量不要超过100行,或其他团队共同商定的行数。
另外,方法长度超过8000个字节码时,将不会被JIT编译成二进制码。
4.2、
5、类设计
5.1、【强制】除了保持兼容性的情况,总是移除无用属性、方法与参数
特别是private的属性、方法、内部类,private方法上的参数,一旦无用立刻移除。信任代码版本管理系统。
6、控制语句
6.1、【推荐】少用if-else方式,多用哨兵语句式以减少嵌套层次
1 |
|
Facebook-Contrib: Style - Method buries logic to the right (indented) more than it needs to be
6.2、【强制】switch的规则
1)在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪一个case为止;
2)在一个switch块内,都必须包含一个default语句并且放在最后,即使它什么代码也没有。
1 | String animal = "tomcat"; |
7、基本类型与字符串
7.1、 数值equals比较的原则
- 【强制】 所有包装类对象之间值的比较,全部使用equals方法比较
- 【强制】 BigDecimal需要使用compareTo()
- 【强制】 Atomic* 系列,不能使用 equals 方法
因为 Atomic* 系列没有覆写 equals 方法。
1 | // RIGHT |
- 【强制】 double 及 float 的比较,要特殊处理
1 |
|
推荐使用“相对误差”的方式比较浮点数。
7.2、【推荐】 double 或 float 计算时有不可避免的精度问题
1 | float f = 0.45f/3; //结果是0.14999999 |
尽量用double而不用float,但如果是金融货币的计算,则必须使用如下选择:
选项1, 使用性能较差的BigDecimal。BigDecimal还能精确控制四舍五入或是其他取舍的方式。
选项2, 在预知小数精度的情况下,将浮点运算放大为整数计数,比如货币以"分"而不是以"元"计算。
8、集合处理
8.1、【强制】不要在foreach循环里进行元素的remove/add操作,remove元素可使用Iterator方式
1 | //WRONG |
8.2、【强制】使用entrySet遍历Map类集合Key/Value,而不是keySet 方式进行遍历
keySet遍历的方式,增加了N次用key获取value的查询。