目录
一、编程规约
(一)命名风格
对于获取对象不管是多个还是单个是否都可以使用get为前缀,后续单词已复数结尾。
(二)常量定义
(三)代码格式
(四)空格格式
(五) OOP 规约
双进度浮点数double使用bigDecimal类
关于 hashCode 和 equals 的处理,遵循如下规则:
ArrayList 的 subList 结果不可强转成 ArrayList,否则会抛出 ClassCastException 异常,即 java.util.RandomAccessSubList cannot be cast to java.util.ArrayList。
使用 Map 的方法 keySet()/values()/entrySet()返回集合对象时,不可以对其进行添加元素操作,否则会抛出 UnsupportedOperationException 异常。(如果要进行操作的话就需要使用对象锁)
Collections 类返回的对象,如:emptyList()/singletonList()等都是 immutablelist,不可对其进行添加或者删除元素的操作。
使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全一致、长度为 0 的空数组。
使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的 add/remove/clear 方法会抛出 UnsupportedOperationException 异常。
asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。Arrays.asList 体现的是适配器模式,只是转换接口,后台的数据仍是数组。
(六) 并发处理
SimpleDateFormat 是线程不安全的类,一般不要定义为 static 变量,如果定义为static,必须加锁,或者使用 DateUtils 工具类。
必须回收自定义的 ThreadLocal 变量,尤其在线程池场景下,线程经常会被复用,如果不清理自定义的 ThreadLocal 变量,可能会影响后续业务逻辑和造成内存泄露等问题。 尽量在代理中使用 try-finally 块进行回收。
(七) 控制语句
在高并发场景中,避免使用”等于”判断作为中断或退出的条件。
(八) 注释规约
(九) 其它
二、异常日志
(一) 异常处理
有try块放到了事务代码中,catch异常后,如果需要回滚事务,一定要注意手动回 滚事务。
不要在finally块中使用return。
(二)日志规约
应用中不可直接使用日志系统(Log4j、Logback)中的API,而应依赖使用日志框架 SLF4J 中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。(补充)
在日志输出时,字符串变量之间的拼接使用占位符的方式。(重点)
避免重复打印日志,浪费磁盘空间,务必在log4j.xml中设置additivity=false。
异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通 过关键字 throws 往上抛出。
三、单元测试
单元测试应该是全自动执行的,并且非交互式的。测试用例通常是被定期执行的,执行过程必须完全自动化才有意义。输出结果需要人工检查的测试不是一个好的单元测试。单元测试中不准使用 System.out 来进行人肉验证,必须使用 assert 来验证。
单元测试代码必须写在如下工程目录:src/test/java,不允许写在业务代码目录下。
四、安全规约
五、MySQL 数据库
is_xxx 的方式命名,数据类型是 unsigned tinyint (1表示是,0表示否)(注意)
表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间 只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
表必备三字段:id, create_time, update_time。
(二) 索引规约
业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引(也不一定)
超过三个表禁止 join。需要 join 的字段,数据类型必须绝对一致;多表关联查询时,保证被关联的字段需要有索引
在 varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度即可(疑惑)
如果有 order by 的场景,请注意利用索引的有序性。order by 最后的字段是组合 索引的一部分,并且放在索引组合顺序的最后,避免出现 file_sort 的情况,影响查询性能。
利用覆盖索引来进行查询操作,避免回表。(重点)
Explain指令的使用
利用延迟关联或者子查询优化超多分页场景。
(三) SQL语句
不要使用count(列名)或count(常量)来替代count(*),count(*)是SQL92定义的 标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。
count(distinct col) 计算该列除 NULL 之外的不重复行数,注意 count(distinct col1, col2) 如果其中一列全为 NULL,那么即使另一列有不同的值,也返回为 0。
当某一列的值全是NULL时,count(col)的返回结果为0,但sum(col)的返回结果 为 NULL,因此使用 sum()时需注意 NPE 问题。
使用 ISNULL()来判断是否为 NULL 值。
不得使用外键与级联,一切外键概念必须在应用层解决。
禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。
(四) ORM 映射
POJO 类的布尔属性不能加 is,而数据库字段必须加 is_,要求在 resultMap 中进行字段与属性之间的映射。
sql.xml 配置参数使用:#{},#param# 不要使用${} 此种方式容易出现 SQL 注入。
六、工程结构
异常抛出规范
分层领域模型规约
(二) 二方库依赖
线上应用不要依赖SNAPSHOT版本(安全包除外)。(疑惑)
二方库里可以定义枚举类型,参数可以使用枚举类型,但是接口返回值不允许使用 枚举类型或者包含枚举类型的 POJO 对象。
(三) 服务器
七、设计规约
常量类全部保存到constant目录下,如果是跨应用共享常量需要放置到二方库中,通常是client.jar中的constant目录下。
(参考wiki)
在等号两边第一个都是空格
int second = (int)first + 2;
method(args1, args2, args3);
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.subtract(b);
BigDecimal y = b.subtract(c);
BigDecimal recommend1 = new BigDecimal("0.1");
BigDecimal recommend2 = BigDecimal.valueOf(0.1);
List list1 = new ArrayList<>(Arrays.asList("qqq", "www", "eee"));
list1.add("aaa1");
list1.forEach(str -> System.out.println(str));
2)Arrays.asList()这个方法,最好不要用于数组转list。一般适用于初始化一个定长list,并赋值。
private static final ThreadLocal df = new ThreadLocal() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
说明:如果是 JDK8 的应用,可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar,DateTimeFormatter 代替 SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe。
解释:如果threadLocal使用在全局的情况下的话通过static定义就不需要考虑回收。
objectThreadLocal.set(userInfo);
try {
// ...
} finally {
objectThreadLocal.remove();
}
说明:如果并发控制没有处理好,容易产生等值判断被“击穿”的情况,使用大于或小于的区间判断条件
说明(补充):一定要注意,在使用了事务如添加了@transaction,你如果使用try-catch捕获了异常,事务就不会回滚,需要你在catch里面进行回滚操作。
说明:try 块中的 return 语句执行成功后,并不马上返回,而是继续执行 finally 块中的语句,如果此处存在 return 语句,则在此直接返回,无情丢弃掉 try 块中的返回点。
反例:
private int x = 0;
public int checkReturn() {
try {
// x 等于 1,此处不返回
return ++x;
} finally {
24/44
Java 开发手册
// 返回的结果是 2
return ++x;
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Test.class);
补充:建议使用@slf4j注解
说明:因为 String 字符串的拼接会使用 StringBuilder 的 append()方式,有一定的性能损耗。使用占位符 仅是替换动作,可以有效提升性能。
正例:logger.debug("Processing trade with id: {} and symbol: {}", id, symbol);
正例:
正例:logger.error(各类参数或者对象 toString() + "_" + e.getMessage(), e);
说明:源码编译时会跳过此目录,而单元测试框架默认是扫描此目录。
常识注意
说明:任何字段如果为非负数,必须是 unsigned。
注意:POJO类中的任何布尔类型的变量,都不要加is前缀,所以需要在
正例:aliyun_admin,rdc_config,level3_name
正例:where a=? and b=? order by c; 索引:a_b_c
反例:索引如果存在范围查询,那么索引有序性无法利用,如:WHERE a>10 ORDER BY b; 索引 a_b 无 法排序。
简单来说就是order by 和搜索尽量使用索引,,如果是范围查询,就没法利用索引的有序性。
说明:如果一本书需要知道第 11 章是什么标题,会翻开第 11 章对应的那一页吗?目录浏览一下就好,这 个目录就是起到覆盖索引的作用。
正例:能够建立索引的种类分为主键索引、唯一索引、普通索引三种,而覆盖索引只是一种查询的一种效 果,用 explain 的结果,extra 列会出现:using index。
https://www.cnblogs.com/DreamDrive/p/7752960.html
说明: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
简单理解:通过子查询,查询出关键字段。在条件中可以拿到上一页最大的那个页数,然后进行查询。
说明:count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。
在使用联合查询的时候就得注意distinct的使用。
正例:使用如下方式来避免 sum 的 NPE 问题:SELECT IFNULL(SUM(column), 0) FROM table;
图中默认上层依赖于下层,箭头关系表示可直接依赖,如:开放接口层可以依赖于 Web 层,也可以直接依赖于 Service 层,依此类推:
开放接口层:从微服务的角度来分析,也就是对外开发的接口,实现RPC接口调用。如thrift接口调用,将逻辑写入到service进行处理对外接口。
终端显示层:各个端的模板渲染并执行显示的层。如JS、JSP、移动端的展示等,简单来说就是前端渲染展示。
Web层:主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理等。简单来首就是项目中的controller层处理前端请求过来的数据。
Service层:相对具体的业务逻辑服务层。
Manager层:通用业务处理层,它有如下特征:
DAO 层:数据访问层,与底层 MySQL、Oracle、Hbase 等进行数据交互。
外部接口或第三方平台:包括其它部门 RPC 开放接口,基础平台,其它公司的 HTTP 接口。调用外部接口,如thrift的client类
(分层异常处理规约)在DAO层,产生的异常类型有很多,无法用细粒度的异常进 行 catch,使用 catch(Exception e)方式,并 throw new DAOException(e),不需要打印日志,因 为日志在 Manager/Service 层一定需要捕获并打印到日志文件中去,如果同台服务器再打日 志,浪费性能和存储。在 Service 层出现异常时,必须记录出错日志到磁盘,尽可能带上参数 信息,相当于保护案发现场。如果 Manager 层与 Service 同机部署,日志方式与 DAO 层处理 一致,如果是单独部署,则采用与 Service 一致的处理方式。Web 层绝不应该继续往上抛异 常,因为已经处于顶层,如果意识到这个异常将导致页面无法正常渲染,那么就应该直接跳转到友好错误页面,加上用户容易理解的错误提示信息。开放接口层要将异常处理成错误码和错误信息方式返回。
DO(Data Object):此对象与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。
DTO(Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象。
BO(Business Object):业务对象,由 Service 层输出的封装业务逻辑的对象。
AO(Application Object):应用对象,在 Web 层与 Service 层之间抽象的复用对象模型,极为贴 近展示层,复用度不高。
VO(View Object):显示层对象,通常是 Web 向模板渲染引擎层传输的对象。
Query:数据查询对象,各层接收上层的查询请求。注意超过 2 个参数的查询封装,禁止使用 Map 类
来传输。
说明:不依赖 SNAPSHOT 版本是保证应用发布的幂等性。另外,也可以加快编译时的打包构建。
每个公司都有不同的要求,根据实际业务拉觉得服务器的配置。
图形类设计规约
完毕!!!