一、编程规范

(一)命名风格

1.代码中的命名不能以下划线或者是美元的符号开始或结束

2.代码中的命名严禁使用拼音和英文混合的方式

3.类名使用UpperCamelCase驼峰命名,但是以下情况除外:DO/BO/DTO/VO/AO/PO/UID等

4.方法名,参数名,成员变量,局部变量都统一使用lowerCamelCase风格,遵从驼峰格式

5.常量的命名全部使用大写,单词之间用下划线隔开,必须表达清楚

6.抽象类命名使用使用Abstract或者Base开头,异常使用Exception结尾;测试类命名以测试的类的名称开始,以Test结尾

可以使用模板模式生成命名

7.类型中与括号紧邻相连来表示数组

定义整形数组int[] arrayDemo

8.POJO类中布尔类型变量都不要加is的前缀,否则部分框架解析会引起序列化错误

表达是与否的值采用is_xxx的命名方式,需要在设置从is_xxx到xxx的映射关系

9.包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数的形式,但是类名如果有复数的含义,类名可以使用复数

10.避免在子父类的成员变量之间,或者不同代码块的局部变量之间采用完全相同的命名,使可读性降低

11.杜绝完全不规范的缩写,避免望文不知义

12.任何自定义编程元素在命名的时候,使用尽量完整的单词组合来表达它的意思

13.在常量和变量命名的时候,表示类型的名词放在词尾,以提升辨识度

startTime,workQueue等等

14.如果模块,接口,类,方法中使用了设计模式,在命名的时候需要体现出具体模式

15.接口类中的方法和属性不要加任何的修饰符号(public也不要加),保持代码的简洁性

16.内部类的实现类用Impl的后缀与接口区别

17.枚举类名加上Enum后缀,枚举成员名称需要全部大小写,单词之间用下划线隔开

18.各层的命名规约

Service/DAO层方法命名规约

获取单个对象的方法用get做前缀

获取多个对象的方法用list做前缀

获取统计值的方法用count做前缀

插入的方法用insert

删除的方法用remove

修改的方法用update

临域模型命名规则

数据对象:xxxDO

数据传输对象:xxxDTO

展示对象:xxxVO

POJO是DO/DTO/BO/VO的统称,禁止命名为xxxPOJO

(二)常量定义

1.不允许出现没有预先定义的常量直接出现在代码中

2.在long或者Long赋值的时候,数值之后使用大写的L,不能是小写的l,小写容易与数字1混淆

3.不要使用一个常量类维护所有的常量,要按照常量功能进行归类,分开维护

4.如果变量值仅在一个固定范围之内变化用enum类型来定义

(三)代码格式

1.左右小括号和字符之间不出现空格,而左大括号之前需要空格

2.if/for/while/switch/do等保留字与括号之间都必须加一个空格

3.任何两目,三目运算符的左右两边都需要加一个空格

4.采用四个空格缩进,禁止使用tab

5.注释的双斜线与注释内容之间有且仅有一个空格

6.在进行强制转换的时候,右括号和强制转换值之间不需要任何空格隔开

7.单行的字符数不超过120个,超过需要换行

8.方法参数在定义和传入的时候,多个参数逗号后面必须加空格

9.IDE的编码设置为UTF-8,IDE文件的换行符使用Unix格式

10.单个方法的总行数不能超过80行

11.没有必要增加若干的空格来使变量的赋值等号与上一行对应位置的等号对齐

12.不同逻辑,不同语义,不同业务的代码之间插入一个空行分割开来以提升可读性

(四)OOP规约

1.避免通过一个类的对象引用访问此类的静态变量或静态方法,直接使用类名来访问即可

2.所有的覆写方法都需要加@Override注解

3.相同参数类型,相同业务含义,才可以使用Java的可变参数(尽量不用可变参数)

4.外部正在调用或者两方库依赖的接口,不允许修改方法签名,避免对接口调用方产生影响

5.不能使用过时的类或者是方法

6.Object的equals方法容易抛出空指针异常,应该使用常量或者确定有值的对象来调用equals

"test".equals(object)

7.所有的整型包装类对象之间值的比较,全部使用equals方法比较

8.浮点数之间的等值判断,基本数据类型不可以用==来比较,包装数据类型不能用equals来进行判断

浮点数采用“尾数+阶码”的编码方式,二进制无法表示出大部分的十进制小数

9.定义数据对象DO类的时候,属性类型要与数据库字段类型相匹配

10.关于基本数据类型与包装类型

所有的POJO类属性必须使用包装数据类型

RPC方法的返回值和参数必须使用包装数据类型

所有的局部变量使用基本数据类型

11.序列化类新增属性的时候,不要修改serialVersionUID字段,避免反序列失败

12.构造方法中禁止加入业务逻辑

13.POJO类必须写toString方法

14.使用索引访问用String的split方法得到的数组的时候,需要做最后一个分隔符之后有无内容的检查,否则会有IndexOutOfBoundsException的风险

15.当一个类有多个构造方法,或者多个同名的方法的时候,这些方法应该按照顺序放置在一起,便于阅读

16.类内方法定义的顺序依次是:公有方法或者是保护方法 > 私有方法 > getter/setter方法

17.setter方法中,参数名称与类成员变量名称一致,this.成员名 = 参数名

18.循环体内,字符串的连接方式,使用StringBuilder的append方法进行扩展

19.final可以声明类,成员变量,方法,以及本地变量

不允许被继承的类,如:String类

不允许修改引用的域对象

不允许覆盖的方法,例如POJO类中的setter方法

不允许运行过程中重新赋值的局部变量

避免上下文重复使用一个变量

20.慎用Object的clone方法来拷贝对象

对象clone方法默认是浅拷贝,如果想实现深拷贝需要覆写clone方法实现域对象的深度拷贝

21.类成员与方法访问控制从严

如果不允许外部直接通过new来创建对象,那么构造方法必须是private

工具类不允许有public构造方法

类非static成员变量并且与子类共享,必须是protected

类非static成员变量并且仅在本类使用,必须是private

类static成员变量如果仅在本类使用,必须是private

如果是static成员变量,考虑是否为final

类成员方法只供类内部调用,必须是private

类成员方法只对继承类公开,那么限制为protected

(五)集合处理

1.关于hashCode和equals的处理

只要是覆写equals,就必须覆写hashCode

因为Set存储的是不重复的对象,一句hashCode和equals进行判断,所以Set存储的对象必须覆写这两个方法

如果自定义对象作为Map的键,那么必须覆写hashCode和equals

2.使用Map的方法返回集合对象的时候,不可以对其进行添加元素操作,否则会抛出异常

3.使用集合转数组的方法,必须使用集合的toArray(T[] array),传入的是类型完全一致,长度为0的空数组

List list = new ArrayList<>(2);
list.add("guan");
list.add("bao");
String[] array = list.toArray(new String[0]);

4.泛型通配符 extends T>来接收返回的数据,此写法的泛型集合不能使用add方法,而 super T>不能使用get方法,作为接口调用赋值的时候容易出错

5.集合初始化的时候,指定集合初始值的大小

(六)并发处理

1.获取单例对象需要保证线程安全,其中的方法也要保证线程安全

2.创建线程或者是线程池请指定有意义的线程名称,方便出错的时候进行回溯

3.线程资源必须通过线程池提供,不允许在应用中自行显式创建线程

减少在创建和销毁线程上消耗的时间以及系统资源的开销

4.线程池通过ThreadPoolExecutor的方式,规避资源耗尽的风险

5.如果使用SimpleDateFormat需要加锁,否则使用DateUtils工具类

6.必须回收自定义的ThreadLocal变量,尽量在try-finally中进行回收

7.高并发的时候,同步调用需要去考量锁的性能损耗

8.对多个资源,数据库表,对象同时加锁的时候,需要保持一致的加锁顺序,否则可能会导致死锁

9.在使用尝试机制来获取锁的方法中,进入业务代码之前需要先判断当前线程是否持有锁。锁的释放规则与锁的阻塞等待方式相同

10.如果每次访问冲突的概率小于20%,推荐使用乐观锁,否则使用悲观锁

11.资金相关的金融敏感信息,使用悲观锁策略

12.volatile如果是多写无法解决线程安全问题

13.ThreadLocal对象使用static修饰,ThreadLocal无法解决共享对象的更新问题

(七)控制语句

1.在一个switch块内,都必须包含一个default语句并且放在最后,即使是什么代码也没有

2.在if/else/for/while/do语句中必须使用大括号

3.在高并发场景中,避免使用“等于”判断作为中断或退出的条件

4.不要在其他表达式中,插入赋值语句

(八)注释规约

1.类,类属性,类方法的注释必须使用Javadoc规范

2.所有的类必须添加创建者和创建日期

3.所有的枚举类型字段必须有注释,说明每个数据项的用途

4.谨慎注释掉代码

(九)其他

1.在使用正则表达式的时候,利用好预编译的功能

2.获取当前毫秒数System.currentTimeMillis();

(十)异常日志

1.捕获异常是为了处理它,不要捕获之后什么都不做直接抛弃

2.有try块放到了事务代码中,catch异常之后,如果需要回滚事务,一定要注意手动回滚事务

3.不要在finally块中使用return

4.捕获异常与抛出异常必须是完全的匹配,或者捕获异常是抛异常的父类

5.防止NPE,是程序员的基本素养

(十一)日志规约

1.应用中应该依赖使用日志 框架SLF4J中的API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一

2.异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过关键字throws往上抛出

3.谨慎的记录日志,要注意日志的输出量的问题,避免将服务器磁盘撑爆

二.单元测试

1.好的单元测试必须遵守AIR原则(自动化,独立性,可重复)

三.安全规约

1.用户敏感数据禁止直接展示,必须对展示数据进行脱敏

2.用户输入的SQL参数严格使用参数绑定或者METADATA字段值限定,防止SQL注入,禁止字符串拼接SQL访问数据库

3.用户请求传入的任何参数必须做有效性验证

4.禁止向HTML页面输出未经安全过滤或未正确转义的用户数据

5.表单,AJAX提交必须执行CSRF安全验证

6.用户自己生成内容的场景必须实现防刷,文本内容违禁词过滤等风控策略

四.MySQL数据库

(一)建表规约

1.表达是与否的概念的字段,必须使用is_xxx的方式命名,数据类型是unsigned tinyint

2.表名,字段名必须使用小写字母或者是数字,禁止使用数字开头

3.表名不使用复数名词

4.禁止使用保留字

5.主键索引名为pk_字段名;唯一索引名为uk_字段名;普通索引名为idx_字段名

6.小数类型为decimal

7.表必备的三个字段:id,create_time,update_time

8.表的命名最好是遵循“业务名称_表的作用"

9.单表的行数超过500w行或者单表容量超过2GB,才推荐进行分库分表

(二)索引的规范

1.超过三个表禁止join

2.利用覆盖索引来进行查询操作,避免回表

3.建组合索引的时候,区分度最高的在最左边

4.创建索引的误解

宁滥勿缺:认为一个查询就需要建一个索引

宁缺毋滥:认为索引会消耗空间,严重拖慢记录的更新以及行的新增速度

(三)SQL语句

1.count(*)会统计值为NULL的行,而count(列名)不会统计此列为NULL值的行

(四)ORM映射

1.不允许直接拿Hashmap和Hashtable作为查询结果集的输出

2.@Transaction事务不要滥用。事务会影响数据库的QPS,另外使用事务的地方需要考虑各个方面的回滚方案

(五)服务器

1.高并发服务器建议调小TCP协议的time_wait超时时间

2.调大服务器所支持的最大文件句柄数

3.给JVM环境参数设置:-XX:+HeapDumpOnOutMemoryError参数,让JVM碰到OOM场景的时候输出dump信息

4.在线上生产环境,JVM的Xms和Xmx设置一样大小的内存容量,避免在GC后调整堆大小带来的压力

5.服务器内部重定向使用forward;外部重定向地址使用URL拼装工具类来生成,否则会带来URL维护不一致的问题和潜在的安全风险