编码规约
- 抽象类名要以Abstract或Base开头,异常类名要以Exception结尾,测试类名要以被测试类名开头、并且以Test结尾
- 定义数组时、类型与[]要紧挨
- boolean类型变量、均不能以is为前缀
- 包名统一小写,采用单数、单词定义(包名不能复数、不能多词组合),类名可以复数(MessageUtils)
- 接口中方法和属性不要加任何修饰符号(比如public/default)
- service和dao接口定义:get、list、count、save/insert、remove/delete、update做前缀
- 领域模型命名:xxxDO、xxxDTO、xxxVO、xxxPOJO
常量定义
- 传输变量,尽量使用包装类型,Long类型变量、要以大写"L"结尾
- 常量类定义规则,需要按照功能分多个常量类、能用枚举的使用枚举
public final class ResultConstants {
private ResultConstants() {
}
public static final String EMOTION = "emotion";
}
如果一个变量值仅在一个固定的范围内变化,那么需要用枚举表示
public enum SeasonEnum {
SPRING(1), SUMMER(2), AUTUMN(3), WINTER(4);
private int seq;
SeasonEnum(int seq){
this.seq = seq;
}
}
代码格式
- 单行字符不允许超过120个字符,超过部分需要换行。换行时、"."或运算符需要跟着进入新行,下边所有行应该有tab=4的缩进
- 单个方法总行数不要超过80行,及时按照功能抽取代码、尽量合理复用
OOP规约
- 对于类中的静态变量,应避免用类引用调用静态变量,全部采用"类名.属性名"的形式,避免没必要的编译器解析成本
- 尽量不用可变参数编程,变量定义要看字知其义
- 包装类型是否相等的判断、都用equals()方法,Integer必须用equals()方法
- 所有实体类属性中的基本数据类型、必须使用包装类。
实体类中的数据,不能有初始化数据,必须要求使用者进行赋值;
所有RPC方法调用的返回值和参数中的基本数据类型、必须使用包装类型;
所有的局部变量的基本数据类型、推荐使用基本数据类型、不排斥包装类型 - RPC使用到的实体类、必须实现序列化,并且两边实体类的序列化值应一致、不允许修改
- 构造方法中禁止加入任何业务逻辑,如有必须、应使用init()方法
- 实体类都应加toString()方法,如果有继承关系、应首先执行:super.toString()
- 实体类中,禁止某一个变量xxx的isXxx()方法和getXxx()方法同时存在
- 类中方法定义顺序:公有方法或保护方法–>私有方法–>getter方法或setter方法
- StringBuiler替代String执行多字符串的拼接操作
- 拷贝对象时、注意深克隆和浅克隆,避免隐含bug
集合处理
- 如果对象使用Set来存储、那么此对象必须重写hashCode()和equals()
- 如果对象最为Map的键、那么此对象必须重写hashCode()和equals()
- 集合的foreach中,不允许remove或add
- 集合定义时,尽可能的指定容量大小
- Map的遍历必须使用entrySet,不能用keySet(遍历了两次:一次是iterator对象,另一次是从map中取值)
- map的k/v空值布局
集合类型 | key | value | super | 说明 |
---|---|---|---|---|
Hashtable | 不允许为 null | 不允许为 null | Dictionary | 线程安全 |
ConcurrentHashMap | 不允许为 null | 不允许为 null | AbstractMap | 锁分段技术(JDK8:CAS) |
TreeMap | 不允许为 null | 允许为 null | AbstractMap | 线程不安全 |
HashMap | 允许为 null | 允许为 null | AbstractMap | 线程不安全 |
并发处理
- 使用Instant代替Date,LocalDateTime代替Calendar,DateTimeFormatter代替SimpleDateFormat
控制语句
- 多层if–else的语句,使用卫语句、策略模式、状态模式等实现
- 对于需要被循环执行的方法,不允许添加参数校验,校验逻辑应放在使用方。
约定规则大于编码,尽量约定某些参数的规范、在调用方处理,被调用者应以性能为主,减少没必要的逻辑
其他
- finally中不允许return,如果有、那么try中的return不会执行了
- 异常不要直接用Exception,定位要精确
- 避免重复打印日志,造成磁盘的浪费
- 页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。索引文件具有 B-Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。
- 利用延迟关联或者子查询优化超多分页场景。
- MySQL 并不是跳过 offset 行,而是取 offset+N 行,然后返回放弃前 offset 行,返回N 行,那当 offset 特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行 SQL 改写。
SELECT a.* FROM 表 1 a, (select id from 表 1 where 条件 LIMIT 100000,20 ) b where a.id=b.id
-
SQL 性能优化的目标:至少要达到 range 级别, 要求是 ref 级别, 如果可以是 consts最好。
说明:
1) consts 单表中最多只有一个匹配行(主键或者唯一索引) ,在优化阶段即可读取到数据。
2) ref 指的是使用普通的索引(normal index) 。
3) range 对索引进行范围检索。
反例: explain 表的结果, type=index,索引物理文件全扫描,速度非常慢,这个 index 级
别比较 range 还低,与全表扫描是小巫见大巫。 -
【参考】创建索引时避免有如下极端误解:
1) 宁滥勿缺。 认为一个查询就需要建一个索引。
2) 宁缺勿滥。 认为索引会消耗空间、严重拖慢更新和新增速度。
3) 抵制惟一索引。 认为业务的惟一性一律需要在应用层通过“先查后插”方式解决。 -
【强制】不要使用 count(列名)或 count(常量)来替代 count(), count()是 SQL92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。
说明: count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。