-
编程命名规范
-
命名规约
-
不能以下划线或美元符号开始,或以下划线或美元符号结束 反例 $Object_ name_ / name$ / Object$
-
严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。 反例:DaZhePromotion [打折] / getPingfenByName() [评分] / int 变量 = 3 正例: ali / alibaba / taobao / cainiao / aliyun / youku / hangzhou 等国际通用的名称,可视为英文。
-
类名开头大写,遵循大驼峰命名规则 反例: userName 正例:UserName
-
方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase (小驼峰)风格,必须遵从驼峰形式。 正例: localValue / getHttpMessage() / inputUserId
-
常量名全部大写,中间_隔开,力求语义表达完整,即是名字很长. 正例:MAX_USER_ID 反例:MAX_ID
-
抽象类名使用Abstract或Base开头, 异常类命名使用Exception结尾,测试以测试名称开始, 以Test结尾 如 AbstractService BaseService UserException UserDaoTest
-
定义数组时 正例: String[] args , 反例:String args[]
-
POJO类中布尔类型的变量,都不要加is,否则会引起框架序列化错误 反例:Boolean isDeleted 在用idea生成getter和setter 是getDeleted和SetDeleted 反射时会以为对应属性是Deleted ,就会抛出异常
-
包名统一使用小写(单数形式),点分隔之间仅能自然语义英文单词,类名可以复数 如 正例:com . alibaba . open . util 、类名为 正例:MessageUtils( 此规则参考spring 的框架结构 )
-
杜绝完全不规范的缩写,避免望文生义 如反例:AbstractClas 缩写为AbsClass , condition 缩写为 condi
-
如果用到了设计模式, 建议在类名中出现具体模式 如正例: LoginProxy OrderFactory
-
接口中的方法和属性不要加任何修饰符号(public 也不要加),但要加上Javadoc注释,尽量不要在接口中定义变量 如 正例: void f(); String COMPABY="alibaba" 说明:java8 允许有默认实现,那么这个default方法,是对所有实现类都有价值的默认实现
-
接口和实现类的命名
-
对于Service和Dao,基于SOA理念,暴露出来的服务一定是接口,内部的实现类用Impl的后缀与接口区别
-
如果接口是形容其能力的, 通常以-able的形式,如AbstractTranslator实现了Translatable接口
-
-
枚举类名建议带上Enum后缀,枚举成员名称大写,单词间用下划线隔开 说明,枚举类构造方法强制私有 正例:类名DealStatusEnum 成员SUCCESS
-
各层命名规范
-
service/dao层命名规范
-
获取单个对象方法用get做前缀
-
获取多个对象方法用list做前缀
-
获取统计值方法用count做前缀
-
插入方法用save(推荐)或insert做前缀
-
删除方法用remove(推荐)或delete做前缀
-
修改方法用update做前缀
-
-
领域模型命名规约
-
数据对象: xxxDO ,xx即为表名
-
数据传输对象:xxx DTO,xxx为业务领域相关名称
-
展示对象:xxxVO ,xxx一般为网页名称
-
POJO是DO/DTO/BO/VO的统称,禁止命名成xxxPOJO.
-
-
-
-
( ( 四) )OOP 规约
-
【强制】避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可
-
【强制】所有的覆写方法,必须加@ Override 注解。
-
【强制】不能使用过时的类或方法
-
推荐使用 java . util . Objects # equals (JDK 7 引入的工具类 ) 进行equals 或compare比较
-
【强制】所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较。
说明:对于 Integer var = ? 在-128 至 127 范围内的赋值, Integer 对象是在
IntegerCache . cache 产生,会复用已有对象,这个区间内的 Integer 值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑(推荐使用 equals 方法进行判断。)
-
所有的 POJO 类属性必须使用包装数据类型 反例 private int a; 正例private Integer a;
-
【强制】定义 DO / DTO / VO 等 POJO 类时,不要设定任何属性默认值
-
【强制】 POJO 类必须写 toString 方法
-
【推荐】使用索引访问用 String 的 split 方法得到的数组时,需做最后一个分隔符后有无内容的检查 String str="a , b , c , , ,"; String[] ary = str.split(","); 就会出错
-
【推荐】 类内方法定义顺序依次是:公有方法或保护方法 > 私有方法 > getter / setter
方法。
-
【推荐】 setter 方法中,参数名称与类成员变量名称一致, this .成员名 = 参数名。在
getter / setter 方法中,不要增加业务逻辑,增加排查问题的难度
-
【推荐】循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展
-
【推荐】 final 可以声明类、成员变量、方法、以及本地变量,下列情况使用 final 关键字:
-
不允许被继承的类,如: String 类。
-
不允许修改引用的域对象,如: POJO 类的域变量。
-
不允许被重写的方法,如: POJO 类的 setter 方法。
-
不允许运行过程中重新赋值的局部变量。
-
避免上下文重复使用一个变量,使用 final 描述可以强制重新定义一个变量,方便更好地进行重构。
-
-
【推荐】慎用 Object 的 clone 方法来拷贝对象 , 对象的 clone 方法默认是浅拷贝
-
【推荐】类成员与方法访问控制从严
-
如果不允许外部直接通过 new 来创建对象,那么构造方法必须是 private 。
-
工具类不允许有 public 或 default 构造方法。
-
类非 static 成员变量并且与子类共享,必须是 protected 。
-
类非 static 成员变量并且仅在本类使用,必须是 private 。
-
类 static 成员变量如果仅在本类使用,必须是 private 。
-
若是 static 成员变量,必须考虑是否为 final 。
-
类成员方法只供类内部调用,必须是 private 。
-
类成员方法只对继承类公开,那么限制为 protected
-
继承用protected , 本类使用 用private
-
-
【强制】关于 hashCode 和 equals 的处理,遵循如下规则:
-
1) 只要重写 equals ,就必须重写 hashCode 。
-
2) 因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须重写这两个方法。
-
3) 如果自定义对象做为 Map 的键,那么必须重写 hashCode 和 equals 。
说明: String 重写了 hashCode 和 equals 方法,所以我们可以非常愉快地使用 String 对象作为 key 来使用
-
-
【强制】使用集合转数组的方法,必须使用集合的 toArray(T[] array) ,传入的是类型完全一样的数组,大小就是 list . size()
-
。【强制】不要在 foreach 循环里进行元素的 remove / add 操作。 remove 元素请使用 Iterator方式,如果并发操作,需要对 Iterator 对象加锁
-
【推荐】集合初始化时,指定集合初始值大小。这样才不至于浪费内存资源
-
-
【参考】利用 Set 元素唯一的特性,可以快速对一个集合进行去重操作,避免使用 List 的contains 方法进行遍历、对比、去重操作。
( ( 七) )控制语句
-
【强制】在一个 switch 块内,每个 case 要么通过 break / return 等来终止,要么注释说明程序将继续执行到哪一个 case 为止 ; 在一个 switch 块内,都必须包含一个 default 语并且放在最后,即使它什么代码也没有。
-
【强制】在 if / else / for / while / do 语句中必须使用大括号。即使只有一行代码,避免使用单行的形式: if (condition) statements;
-
【推荐】表达异常的分支时,少用 if-else 方式 ,这种方式可以改写成 ,全部用if if if
( 八) )注释规约
1. 【强制】类、类属性、类方法的注释必须使用 Javadoc 规范,使用/**内容*/格式,不得使用// xxx 方式。
说明:在 IDE 编辑窗口中, Javadoc 方式会提示相关注释,生成 Javadoc 可以正确输出相应注释 ; 在 IDE 中,工程调用方法时,不进入方法即可悬浮提示方法、参数、返回值的意义,提高阅读效率。
2. 【强制】所有的抽象方法 ( 包括接口中的方法 ) 必须要用 Javadoc 注释、除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。
说明:对子类的实现要求,或者调用注意事项,请一并说明。
3. 【强制】所有的类都必须添加创建者和创建日期。
4. 【强制】方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释使用/* */注释,注意与代码对齐。
5. 【强制】所有的枚举类型字段必须要有注释,说明每个数据项的用途。
6. 【推荐】与其"半吊子"英文来注释,不如用中文注释把问题说清楚。专有名词与关键字保持英文原文即可。
反例:" TCP 连接超时"解释成"传输控制协议连接超时",理解反而费脑筋。
7. 【推荐】代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑等的修改。
说明:代码与注释更新不同步,就像路网与导航软件更新不同步一样,如果导航软件严重滞后,就失去了导航的意义。
8. 【参考】合理处理注释掉的代码。在上方详细说明,而不是简单的注释掉。如果无用,则删除。说明:代码被注释掉有两种可能性:
1 ) 后续会恢复此段代码逻辑。2 ) 永久不用。前者如果没有备注信息,难以知晓注释动机。后者建议直接删掉 ( 代码仓库保存了历史代码 ) 。
9. 【参考】对于注释的要求:第一、能够准确反应设计思想和代码逻辑 ; 第二、能够描述业务含义,使别的程序员能够迅速了解到代码背后的信息。完全没有注释的大段代码对于阅读者形同天书,注释是给自己看的,即使隔很长时间,也能清晰理解当时的思路 ; 注释也是给继任者看的,使其能够快速接替自己的工作。
10. 【参考】好的命名、代码结构是自解释的,注释力求精简准确、表达到位。避免出现注释的一个极端:过多过滥的注释,代码的逻辑一旦修改,修改注释是相当大的负担。
反例:
// put elephant into fridge
put(elephant, fridge);
方法名 put ,加上两个有意义的变量名 elephant 和 fridge ,已经说明了这是在干什么,语义清晰的代码不需要额外的注释。
11. 【参考】特殊注释标记,请注明标记人与标记时间。注意及时处理这些标记,通过标记扫描,经常清理此类标记。线上故障有时候就是来源于这些标记处的代码。
1 ) 待办事宜 (TODO) : ( 标记人,标记时间, [ 预计处理时间 ])
表示需要实现,但目前还未实现的功能。这实际上是一个 Javadoc 的标签,目前的 Javadoc
还没有实现,但已经被广泛使用。只能应用于类,接口和方法 ( 因为它是一个 Javadoc 标签 ) 。
2 ) 错误,不能工作 (FIXME) : ( 标记人,标记时间, [ 预计处理时间 ])
在注释中用 FIXME 标记某代码是错误的,而且不能工作,需要及时纠正的情况
二 异常日志
-
【强制】不能在 finally 块中使用 return , finally 块中的 return 返回后方法结束执行,不会再执行 try 块中的 return 语句。
-
【强制】有 try 块放到了事务代码中, catch 异常后,如果需要回滚事务,一定要注意手动回滚事务。
-
【推荐】防止 NPE ,是程序员的基本修养,注意 NPE 产生的场景:
-
1 ) 返回类型为基本数据类型,return 包装数据类型的对象时,自动拆箱有可能产生 NPE。反例: public int f() { return Integer 对象}, 如果为 null ,自动解箱抛 NPE
-
2 ) 数据库的查询结果可能为 null 。
-
3 ) 集合里的元素即使 isNotEmpty ,取出的数据元素也可能为 null 。
-
4 ) 远程调用返回对象时,一律要求进行空指针判断,防止 NPE 。
-
5 ) 对于 Session 中获取的数据,建议 NPE 检查,避免空指针。
-
6 ) 级联调用 obj . getA() . getB() . getC(); 一连串调用,易产生 NPE 。
正例:使用 JDK8 的 Optional 类来防止 NPE 问题
-
三、M My y SQL 数据 数据库
( ( 一) ) 建表 建表规约 规约
-
【强制】表达是与否概念的字段,必须使用 is _ xxx 的方式命名,数据类型是 unsigned tinyint( 1 表示是,0 表示否 ) 。说明:任何字段如果为非负数,必须是 unsigned 。正例:表达逻辑删除的字段名 is_deleted ,1表示删除,0 表示未删除。
-
【强制】表名、字段名必须使用小写字母或数字 , 禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
正例: getter _ admin , task _ config , level 3_ name
反例: GetterAdmin , taskConfig , level _3_ name
-
【强制】表名不使用复数名词
-
【强制】禁用保留字,如 desc 、 range 、 match 、 delayed 等,请参考 MySQL 官方保留字
-
【强制】主键索引名为 pk_ 字段名;唯一索引名为 uk _字段名 ; 普通索引名则为 idx _字段名。说明: pk_ 即 primary key;uk _ 即 unique key;idx _ 即 index 的简称
-
【强制】小数类型为 decimal ,禁止使用 float 和 double 。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。
-
【强制】如果存储的字符串长度几乎相等,使用 char 定长字符串类型
-
【强制】 varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度大于此值,定义字段类型为 text ,独立出来一张表,用主键来对应,避免影响其它字段索引效率。
-
【强制】表必备三字段: id , gmt _ create , gmt _ modified 。
说明:其中 id 必为主键,类型为 unsigned bigint 、单表时自增、步长为 1。
gmt _ create ,gmt _ modified 的类型均为 datetime 类型。
gmt + 8 是北京时间 , Greenwich Mean Time 格林尼治标准时间;
-
【推荐】表的命名最好是加上"业务名称_表的作用"。
正例: tiger _ task / tiger _ reader / mpp _ config
-
【推荐】单表行数超过 500 万行或者单表容量超过 2 GB ,才推荐进行分库分表。
说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。
-
【参考】合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索速度
( ( 二) ) 索引规约
-
. 【强制】业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引。
说明:不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明显的 ; 另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。
-
【强制】 超过三个表禁止 join 。需要 join 的字段,数据类型必须绝对一致 ; 多表关联查询时,保证被关联的字段需要有索引。
-
【强制】页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。
说明:索引文件具有 B - Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。
-
【推荐】如果有 order by 的场景,请注意利用索引的有序性。 order by 最后的字段是组合索引的一部分,并且放在索引组合顺序的最后,避免出现 file _ sort 的情况,影响查询性能。正例: where a =? and b =? order by c; 索引: a _ b _
-
【推荐】利用延迟关联或者子查询优化超多分页场景。
说明: MySQL 并不是跳过 offset 行,而是取 offset + N 行,然后返回放弃前 offset 行,返回N 行,那当 offset 特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行 SQL 改写。
正例:先快速定位需要获取的 id 段,然后再关联:
SELECT a.* FROM 表 1 a, (select id from 表 1 where 条件 LIMIT 100000,20 ) b where a.id=b.id
-
【参考】创建索引时避免有如下极端误解:
-
1 ) 宁滥勿缺。误认为一个查询就需要建一个索引。
-
2 ) 宁缺勿滥。误认为索引会消耗空间、严重拖慢更新和新增速度。
-
3 ) 抵制惟一索引。误认为业务的惟一性一律需要在应用层通过"先查后插"方式解决
-
SQL语句
-
【强制】不要使用 count( 列名 ) 或 count( 常量 ) 来替代 count( * ) , count( * ) 是 SQL 92 定义的
标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。
说明: count( * ) 会统计值为 NULL 的行,而 count( 列名 ) 不会统计此列为 NULL 值的行。
-
【强制】当某一列的值全是 NULL 时, count(col) 的返回结果为 0,但 sum(col) 的返回结果为
NULL ,因此使用 sum() 时需注意 NPE 问题。
-
【强制】不得使用外键与级联,一切外键概念必须在应用层解决。
说明: ( 概念解释 ) 学生表中的 student _ id 是主键,那么成绩表中的 student _ id 则为外键。如果更新学生表中的 student _ id ,同时触发成绩表中的 student _ id 更新,则为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群 ; 级联更新是强阻塞,存在数据库更新风暴的风险 ; 外键影响数据库的插入速度。
-
【强制】禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。
-
【参考】如果有全球化需要,所有的字符存储与表示,均以 utf -8 编码,注意字符统计函数的区别。
ORM映射
-
【强制】在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明。
说明:1 ) 增加查询分析器解析成本。2 ) 增减字段容易与 resultMap 配置不一致。
-
【强制】 POJO 类的 布尔 属性不能加 is ,而数据库字段必须加 is _,要求在 resultMap 中进行字段与属性之间的映射。
说明:参见定义 POJO 类以及数据库字段定义规定,在 <resultMap>中 增加映射,是必须的。在 MyBatis Generator 生成的代码中,需要进行对应的修改
-
【强制】sql. xml 配置参数使用:#{},# param # 不要使用${} 此种方式容易出现 SQL 注入。
-
【强制】 iBATIS 自带的 queryForList(String statementName , int start , int size) 不推
荐使用。说明:其实现方式是在数据库取到 statementName 对应的 SQL 语句的所有记录,再通过 subList取 start , size 的子集合。
正例: Map<String, Object> map = new HashMap<String, Object>();
map.put("start", start);
map.put("size", size);
-
【强制】更新数据表记录时,必须同时更新记录对应的 gmt _ modified 字段值为当前时间。
-
【参考】@ Transactional 事务不要滥用。事务会影响数据库的 QPS ,另外使用事务的
地方需要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等