我们引用阿里的Java开发手册作为后端开发规范要求。
这里是它的地址:
- 官方说明 https://github.com/alibaba/p3c
- IDEA规约插件 https://github.com/alibaba/p3c/blob/master/idea-plugin
- 中文版手册 Java开发手册(黄山版).pdf(发行日期:2022年2月3日)
以下按照正文结构进行要点总结(更详细的必须阅读原文)。
此外,这个开发手册还涉及到了和前端开发以及数据库设计有关的内容,也需要我们加以学习。
一、编程规约
(一)命名风格
- 禁止中英文混用的命名,除非是具有国际化语义的专有名词。例如:alibaba、taobao。推荐IDEA安装翻译插件【Translation】
- 包名统一小写,统一使用单数形式
- 类名统一使用大驼峰形式,可以使用复数形式,含设计模式/使用语义的要在命名上有所体现。特殊情况:DO / BO / DTO / VO / AO / PO / UID 等
- POJO类中,不要使用is_xxx命名风格的布尔型变量,否则其取值方法isXxx()容易令框架序列化错误
- 不要采用随意的英文缩写,尤其不要再前后端交互的变量上,增加沟通成本。特殊情况:msg等
- 枚举类带上Enum后缀,在引用枚举的String属性上通过
{@link Enum}
的形式标明。枚举类应按业务场景,分包维护起来,不要大而全。 - (推荐)除有明显的时间态,有根据数字大小判断进程的类型字段,例如配送状态以外,尽量减少直接使用数字作为枚举。
service/dao层方法以动词开头,获取单个对象以get做前缀,多个对象以list做前缀等
(二)常量定义
不允许任何魔法值。魔法值会随着业务发展越藏越深,产生问题
- 注意常量的访问限制符,不要无脑public
同一个工程的通用枚举,应统一维护。例如“Y/N”、“YES/NO”
(三)代码格式
注意空格/换行等问题,写完一个方法,及时格式化一下代码。IDEA快捷键“CTRL+ALT+T”
- 二目/三目运算符左右需要空格,不要全部挤在一块
- 注释的双斜线和注释内容间有且仅有一个空格
- 注释掉的代码块,如果有恢复的可能,需要使用三个斜线进行说明,例如:
/// 等待开启活动
- 单行代码字符不超120个,注意换行规范
- 单个方法行数不超过80行。推荐使用IDEA提供的refactor->extract功能,重构代码,分清红花与绿叶。注意提取出来方法的访问修饰符要不能无脑public/private
- 方法参数在定义和传入时,多个参数逗号后面必须加空格
- 不同逻辑/语义/业务间的代码,用一个空行间隔就够了,不必要多个换行
出现多组重复代码时,要考虑进行代码重构。getter方法的使用也能重构。
(四)OOP规约
尽量不要使用过期的类/方法,使用前需要了解其替代方案
- 提供的参数/方法过期时,及时标记@Deprecated,并说明过期原因和新方案
- 使用Object的equals方法时,需要将常量/确定有值的对象作为左操作对象,避免NPE
- 基本类型的包装类进行数组比较时,也使用equals方法
- (阿里强制)货币金额,数据类型均以最小货币单位整型存储。例如分。
- 浮点数,包括float及其包装类Float的数值比较,不能单纯用==或equals判断。解决方案:一、规定一个最小允许差值;二、使用BigDecimal进行转换
- BigDecimal对象的数值比较,不能单纯使用equals方法,应使用compareTo()方法,因为BigDecimal对象属性包括数值和小数范围scale
- 禁止使用构造方法 BigDecimal(double)的方式把 double 值转化为 BigDecimal 对象。推荐使用BigDecimal.valueOf()方法或则入参为字符串的构造方法
关于基本数据类型和包装数据类型选择:
1) 【强制】所有的 POJO 类属性必须使用包装数据类型。 2) 【强制】RPC 方法的返回值和参数必须使用包装数据类型。 3) 【推荐】所有的局部变量使用基本数据类型。
(阿里强制)定义 DO/DTO/VO 等 POJO 类时,不要设定任何属性默认值。
说明:如果确实有使用需求,注意@Builder标注的类,不能直接采用“属性 = 初始值”的方式赋值,需要标记@Builder.default,并配合.build()方法使用。
new出来的对象和build出来的对象,其默认值策略有冲突。
- 有序列化需求的类,如缓存到redis、存到文件上等,统一加上 serialVersionUID 字段,避免版本变更引起序列化异常
- 循环体内连接字符串,推荐使用StringBuilder
类成员和方法的访问权限需要控制从严,按需公开,减少改代码的风险
(五)日期时间
日期格式化pattern中,注意大小写。标准格式“yyyy-MM-dd HH:mm:ss”
- 避免使用写死的月份天数、年份天数等
-
(六)集合处理
Set集合和Map的key值,都需要进行equals和hashCode方法的重写。平时用的String类已重写了。
- Map的keySet()/values()/entrySet()方法等返回集合对象时,不可对其进行元素添加/删除操作
- Collections的emptyList()/singletonList()返回值,也不可对其进行元素添加/删除操作
- 工具类Arrays.asList()方法返回的集合,不能使用元素修改操作
- 集合转数组的方法,必须使用集合的toArray(T[] array),传入的是类型完全一致、长度为 0 的空数组
- 使用Collection的实现类的addAll方法,需要对入参进行NPE校验
- 不要在集合的foreach循环中进行remove/add操作。请使用iterator方式
- 遍历Map推荐采用Map.entrySet或则Map.forEach方法
- 可以利用Set的元素唯一特性进行数据去重操作
(补充)Java8及以上推荐使用流特性进行集合操作,简化代码
(七)并发处理
单例模式需要进行并发控制,推荐使用二重锁
- 线程资源应由线程池提供,不要手动创建新线程
- SimpleDateFormat是线程不安全的类,可以使用ThreadLocal规避,也推荐使用JDK8及以上的DateTimeFormatter(线程安全)
-
(八)控制语句
switch语句,每一个case,要有明确的break/return。switch末尾,应有default处理
- switch语句需要对入参进行空值判断,避免业务错误
- 三目运算符
condition ? 表达式1:表达式2
中,表达式1和表达式2需要使用相同类型,避免拆箱产生数据异常 - 减少
if()...else if()...else...
等表达式的使用,尽可能采用“卫语句” - if语句内不要使用复杂表达式,如有必要,请进行变量提取
- 赋值语句单独占一行,不要搞捉迷藏
- 不要使用“反向逻辑”,尽量都使用“正向逻辑”
- 公共接口需要做入参保护,尤其是批量操作接口,需要考虑阈值问题
-
(九)注释规约
类、类属性、类方法的注释需要使用Javadoc规范。(补充:设计第三方的POJO类更加需要注释)
- 接口以及抽象方法,必须使用Javadoc注释,说明该方法的业务逻辑。(补充:可以配合{@link}以及{@see}等注释说明)
- 不要使用行尾注释。方法内的单行注释写在被注释代码上方,独占一行,//后面有且仅有一个空格
- 代码的更新需要及时同步注释内容,尤其是修正接口注释,修正方法名等
- 利用代码重构的艺术,尽量做到代码自解释
-
(十)前后端规约
前后端交互的JSON数据,属性名应是小驼峰风格。(补充:第三方的一些特殊属性可以例外,如openid)
- 对于需要使用超大整数的场景,后端采用String字符串类型返回,不要使用Long类型,防止数据范围丢失
- (推荐)前后端的时间格式统一为“yyyy-MM-dd HH:mm:ss”,统一为GMT
-
(十一)其他
-
二、异常日志
(一)错误码
(二)异常处理
能预检的异常,例如NPE,不要通过catch的方式处理
- 不要试图通过异常捕获进行流程控制
- 不要对大块代码进行try-catch操作,稳定代码不要包裹进去。catch的异常也应尽可能进行类型区分
- 异常catch下来之后,必须合理处理,内部无法处理请再业务记录/处理后抛给上层,并在方法注释声明
- 事务场景下catch了异常,如需要事务回滚,请手动回滚或抛出异常
调用RPC、二方包等相关方法时,捕获异常必须使用Throwable类来进行拦截。(补充:需与自身服务异常进行区分)
(三)日志规约
不要直接使用日志系统实现的API,应应该使用门面模式的日志框架,推荐使用SLF4J
- 日志打印时禁止使用JSON工具将对象转换成String,防止getter方法重写导致异常
- 字符串使用{}占位
- 英文日志如果描述不清楚,请使用中文日志
- catch异常进行error日志记录,如
log.error("XXX异常", e )