一、编程规约
(一)命名规约
1、类名驼峰、领域模型除外VO、BO、DTO、DO统称POJO
4、数组String[] args
8、枚举类 Enum ,其实就是特殊的常量类,构造方法强制私有
( 二 )常量定义
4、如果变量值仅在一个范围内变化用 Enum 类;public Enum{ MONDAY(1)、、 SUNDAY(7);}
(三)格式规约
4、单行字符数不得超过120,超出换行,换行缩进 4 个空格,并且方法前的点符号一起换行
(四)OOP
4、对外暴露的接口签名,原则上不允许修改方法签名,接口过时加@Deprecated
13、类内方法定义顺序依次是:公有方法或保护方法 > 私有方法 > getter/setter方法。
16、慎用 Object 的 clone 方法来拷贝对象。其浅拷贝,如果要用,则重写。
18、任何类、方法、参数、变量,严控访问范围。过宽泛的访问范围,不利于模块解耦。变量作用域太大。例如delete的方法,一定要思考能否不能public/这其实是遵循了最少特权原则 //构造方法私有化,一般单利模式用的多,此时使用getInstance获取实例对象
(五)集合
2、ArrayList的subList结果不可强转成ArrayList
3、使用集合转数组的方法,必须使用集合的 toArray(T[] array),直接使用 toArray 无参方法存在问题,此方法返回值只能是 Object[]类,若强转其它类型数组将出现 ClassCastException 错误。
4、使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的 add/remove/clear 方法会抛出 UnsupportedOperationException 异常。
11、利用 Set 元素唯一的特性,可以快速对一个集合进行去重操作,避免使用 List 的contains 方法进行遍历、对比、去重操作。
( 六 )并发
4、线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则Executors
10、使用 CountDownLatch 进行异步转同步操作,每个线程退出前必须调用 countDown方法
11、避免 Random 实例被多线程使用。在 JDK7 之后,可以直接使用 API ThreadLocalRandom,在 JDK7 之前,可以做到每个线程一个实例。
13、volatile 解决多线程内存不可见问题。如果是 JDK8,推荐使用 LongAdder 对象,比 AtomicLong 性能更好(减少乐观锁的重试次数)。
14、HashMap 在容量不够进行 resize 时由于高并发可能出现死链,导致 CPU 飙升,在开发过程中注意规避此风险。
15、ThreadLocal 无法解决共享对象的更新问题,ThreadLocal 对象建议使用 static修饰。
( 七 )控制语句
1、在一个 switch 块内,都必须包含一个 default 语句并且放在最后,即使它什么代码也没有。
3、逻辑上超过 3 层的 if-else 代码可以使用卫语句,或者状态模式来实现。
7、需要参数校验的场景:低频、执行时间长、高稳定高可用、对外接口、权限接口
8、方法中不需要参数校验的场景:循环、底层如DAO频度高、private(循底私)
(八)注释
6、所有的枚举类型字段必须要有注释,说明每个数据项的
8、特殊注释标记,待办事宜(TODO),错误,不能工作(FIXME)
(九)Other
1、在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度。
二、异常日志
( 一 ) 异常处理
6、finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch。jdk1.7后有了,try-with-resource,真的很骚
12、定义时区分 unchecked / checked 异常,避免直接使用 RuntimeException 抛出,更不允许抛出 Exception 或者 Throwable,应使用有业务含义的自定义异常。推荐业界已定义过的自定义异常,如:DAOException / ServiceException 等。
13、避免出现重复的代码(Don’t Repeat Yourself),即 DRY 原则
( 二)日志
1、应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架SLF4J 中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
2、异常日志文件最少15天
4、对 trace/debug/info 级别的日志输出,必须使用条件输出形式或者使用占位符的方式。if (logger.isDebugEnabled()) { }
6、异常信息应该包括两类信息:案发现场信息和异常堆栈信息。logger.error(各类参数或者对象 toString + "_" + e.getMessage(), e);
7、可以使用 warn 日志级别来记录用户输入参数错误的情况,避免用户投诉时,无所适从。
三、安全规约
5、表单、 AJAX 提交必须执行 CSRF 安全验证。
四、MySQL
(一)建表
1、表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint( 1 表示是,0 表示否)
5、禁用保留字,如 desc、range、match、delayed
6、唯一索引名为 uk_字段名;普通索引名则为 idx_字段名
7、小数类型为 decimal,禁止使用 float 和 double。
8、如果存储的字符串长度几乎相等,使用 char 定长字符串类型
11、表的命名最好是加上“业务名称_表的作用”。
14、单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表
(二)索引
2、超过三个表禁止 join。需要 join 的字段,数据类型保持绝对一致;多表关联查询时,保证被关联的字段需要有索引
3、在 varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度。(mysql的索引都是排好序的。如果区分度高排序越快)。索引区分度公式:select count(distinct left(列名,'索引长度'))/count(*) from table;
4、页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。索引文件具有 B-Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。
5、如果有 order by 的场景,请注意利用索引的有序性。order by 最后的字段是组合索引的一部分,并且放在索引组合顺序的最后,避免出现 file_sort 的情况,影响查询性能。正例:where a=? and b=? order by c; 索引:a_b_c。索引中有范围查找,那么索引有序性无法利用,如:WHERE a>10 ORDER BY b; 索引a_b 无法排序。
6、利用覆盖索引来进行查询操作,来避免回表操作。(如果一个索引包含(或覆盖)所有需要查询的字段的值,称为‘覆盖索引’。即只需扫描索引而无须回表。)
8、SQL 性能优化的目标:至少要达到 range 级别,要求是 ref (normal index)级别,如果可以是 consts(主键或者唯一索引)最好。explain 表的结果,type=index,索引物理文件全扫描,速度非常慢,这个 index 级别比较 range 还低,与全表扫描是小巫见大巫。
9、建组合索引的时候,区分度最高的在最左边。
(三)SQL规约
1、【强制】不要使用 count(列名)或 count(常量)来替代 count(*),count(*)就是 SQL92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。说明:count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。
2、count(distinct col),不会计算空值
3、使用sum()时需注意NPE问题,因为col全为null时,返回null
4、使用ISNULL()来判断是否为NULL值。
6、不得使用外键与级联,外键概念应在应用层解决。例如更新学生表的学生id,同时出发成绩表的学生id更新,即为级联更新。外键与级联适合单机,不适合分布式、高并发集群,外键影响数据库插入速度
7、禁止使用存储过程,存储过程没有移植性、难以调试和拓展
9、in里面的数据集控制在1000以内
(四)ORM映射
4、@Transactional会影响QPS。另外在使用事务的地方,要考虑缓存回滚、消息补偿、统计修正、搜索引擎回滚
五、工程结构
2、高并发服务器建议调小 TCP 协议的 time_wait 超时时间。操作系统默认 240 秒后,才会关闭处于 time_wait 状态的连接,在高并发访问下,服务器端会因为处于 time_wait 的连接数太多,可能无法建立新的连接。在 linux 服务器上请通过变更/etc/sysctl.conf 文件去修改该缺省值
3、调大服务器所支持的最大文件句柄数FD。主流操作系统的设计是将 TCP/UDP 连接采用与文件一样的方式去管理,即一个连接对应于一个 fd。 主流的 linux 服务器默认所支持最大 fd 数量为 1024,当并发连接数很大时很容易因为 fd 不足而出现“open too many files”错误
4、给 JVM 环境参数设置-XX:+HeapDumpOnOutOfMemoryError 参数,让 JVM 碰到 OOM 场景时输出 dump 信息。
5、在线上生产环境, JVM 的 Xms 和 Xmx 设置一样大小的内存容量, 避免在 GC 后调整堆大小带来的压力
六、设计规约
1、类在设计与实现时要符合单一原则。
2、谨慎使用继承的方式来进行扩展,优先使用聚合/组合的方式来实现。不得已使用继承的话,必须符合里氏代换原则,此原则说父类能够出现的地方子类一定能够出现,比如, “把钱交出来” ,钱的子类美元、欧元、人民币等都可以出现。
3、系统设计时,根据依赖倒置原则,尽量依赖抽象类与接口,有利于扩展与维护。
4、系统设计时,注意对扩展开放,对修改闭合(开闭原则)。极端情况下,交付的代码都是不可修改的,同一业务域内的需求变化,通过模块或类的扩展来实现。
6、敏捷开发主要是省去多余的设计方案和审批流程
7、系统设计主要目的是明确需求、理顺逻辑、后期维护,次要目的为了指导编码
8、设计的本质是为了识别和表达系统难点,找到系统的变化点,隔离变化点。其实设计的目的都是隔离系统变化点。
9、系统架构设计目的 1、明确系统边界。确定系统在技术层面的做与不做 2、确定系统内部模块之间的关系。命定模块之间的依赖关系及模块宏观的输入输出 3、确定指导后续设计与演化的原则。使后续的子系统或模块设计在规定框架内继续演化 4、确定非功能性需求。例如安全性、可用性、可拓展性。