你去k
前言
咱们好,我是田螺。
咱们开发完需求,提测前,一般都需求代码评定。小伙伴们,你们知道代码评定,一般都有哪些军规嘛?今日田螺哥给你带来代码评定的18个军规。
- 大众号:捡田螺的小男孩 (有田螺精心原创的面试PDF)
- github地址,感谢每颗star:github
1. 增加必要的注释
其实,写代码的时分,没有必要写太多的注释,因为好的办法名、变量名,便是最好的注释。以下便是笔者总结的一些注释标准:
- 一切的类都必须增加创建者和创建日期,以及简略的注释描绘
- 办法内部的杂乱事务逻辑或许算法,需求增加清楚的注释
- 一般状况下,注释描绘类、办法、变量的作用
- 任何需求提醒的警告或
TODO
,也要注释清楚 - 假如是注释一行代码的,就用
//
;假如注释代码块或许接口办法的,有多行/* **/
- 一块代码逻辑假如你站在一个陌生人的角度去看,第一遍看不懂的话,就需求增加注释了
以下便是一些增加注释的demo:
/**
* @author 田螺
* @date 2023/04/22 5:20 PM
* @desc 田螺的完成类,捡田螺、卖田螺 (更多干货,重视大众号:捡田螺的小男孩)
*/
public class TianLuoClass {
/**
* 这是卖田螺的两法,它将两个田螺的价格整数相加并回来成果。
*
* @param x 第一个整数
* @param y 第二个整数
* @return 两个整数的和
*/
public int sellTianLuo(int x, int y) {
return x + y;
}
}
2.日志打印标准
日志是快速定位问题的好帮手,是撕逼和甩锅的利器!打印好日志十分重要。假如代码评定的时分,这些日志标准没恪守,就需求修改:
- 日志等级挑选不对。常见的日志等级有
error、warn、info、debug
四种,不要反手便是info
哈 - 日志没打印出调用办法的入参和呼应成果,尤其是跨体系调用的时分。
- 事务日志没包括要害参数,如
userId,bizSeq
等等,不方便问题排查 - 假如日志包括要害信息,比方手机号、身份证等,需求脱敏处理
- 一些不符合预期的状况,如一些不知道反常(数据库的数据反常等),又或许不符合事务预期的特殊场景,都需求打印相关的日志
对于日志打印标准,我之前整理出一篇文章,咱们能够看一下哈,挺有用的:
工作总结!日志打印的15个主张
3. 命名标准
Java代码的命名应该明晰、简练和易于了解。咱们代码评定的时分,要留意是否有命名不标准,不明晰的代码。下面是一些命名标准的主张:
- 类和接口应该运用首字母大写的驼峰命名法
- 办法和变量应该运用小写的驼峰命名法
- 常量应该运用全大写字母和下划线
- 开发者是不是挑选易于了解的名称给变量、类和办法进行命名
4.参数校验
咱们代码评定的时分,要留意参数是否都做了校验,如userId
非空查看、金额规模查看、userName
长度校验等等。一般咱们在处理事务逻辑的时分,要遵从先查看、后处理
的准则。
假如你的数据库字段userName设置为
varchar(16)
,对方传了一个32
位的字符串过来,你不校验参数,刺进数据库直接反常了。
许多bug都是因为没做参数校验造成的,这一军规,是代码评定重点重视的哈:
5. 判空处理
- 获取对象的属性时,都要判空处理。要不然许多时分会呈现空指针反常。
if(object!=null){
String name = object.getName();
}
假如你要遍历列表,也需求判空
if (CollectionUtils.isNotEmpty(tianLuolist)) {
for (TianLuo temp : tianLuolist) {
//do something
}
}
6. 反常处理标准
杰出的反常处理能够保证代码的可靠性和可维护性。因而,反常处理也是代码评定的一项重要标准。以下是一些反常处理的主张:
- 不要捕获通用的
Exception
反常,而应该尽可能捕获特定的反常 - 在捕获反常时,应该记载反常信息以便于调试
- 内部反常要承认终究的处理方式,防止不知道反常当作失利处理。
- 在
finally
块中释放资源,或许运用try-with-resource
- 不要运用
e.printStackTrace()
,而是运用log
打印。 -
catch
了反常,要打印出详细的exception
,不然无法更好定位问题 - 捕获反常与抛出反常必须是完全匹配,或许捕获反常是抛反常的父类
- 捕获到的反常,不能疏忽它,要打印相对应的日志
- 留意反常对你的代码层次结构的侵染(早发现早处理)
- 自定义封装反常,不要丢掉原始反常的信息
Throwable cause
- 留意反常匹配的次序,优先捕获详细的反常
- 对外供给APi时,要供给对应的错误码
- 体系内部应该抛出有事务意义的自定义反常,而不是直接抛出
RuntimeException
,或许直接抛出Exception\Throwable
。
咱们有爱好能够看下之前我的这篇文章哈:Java 反常处理的十个主张
7. 模块化,可扩展性
代码评定的时分,重视一下,代码编写规划是否满意模块话,接口是否具有可扩展性
比方你的需求是酱紫:是用户增加或许修改员工时,需求刷脸。那你是反手供给一个员工管理的提交刷脸信息接口?仍是先考虑:提交刷脸是不是通用流程呢?比方转账或许一键贴现需求接入刷脸的话,你是否需求从头完成一个接口呢?仍是当前按事务类型区分模块,复用这个接口就好,保留接口的可扩展性。
假如按模块区分的话,未来假如其他场景比方一键贴现接入刷脸的话,不必再搞一套新的接口,只需求新增枚举,然后复用刷脸经过流程接口,完成一键贴现刷脸的差异化即可。
8. 并发操控标准
- 在运用并发集合时,应该留意它们的线程安全性和并发功能,如
ConcurrentHashMap
是线性安全的,HashMap
便对错线性安全的 - 达观锁,失望锁防止数据库并发.达观锁一般用版别号
version
操控,失望锁一般用select …for update
- 假如是单实例的多线程并发处理,一般经过Java锁机制,比方
sychronized ,reentrantlock
- 假如是同一集群的多线程并发处理,能够用
Redis
分布式锁或许走zookeeper
- 假如是跨集群的多线程并发处理,则考虑数据库完成的分布式锁。
- 在运用分布式锁的时分,要留意有哪些坑,比方redis一些经典的坑.
至于分布式锁,咱们能够看下我之前的这几篇文章哈
- Redis分布式锁的10个坑
- 面试必备:聊聊分布式锁的多种完成!
9. 单元测验标准
- 测验类的命名,一般以测验的类+
Test
,如:CalculatorTest
. - 测验办法的命名,一般以
test
开头+ 测验的办法,如testAdd
. - 单测行覆盖率一般要求大于
75%
. - 单测一般要求包括主流程用例、参数鸿沟值等校验用例
- 单测一般也要求包括中间件拜访超时、回来空、等反常的用例,比方拜访数据库或许
Redis
反常. - 单测用例要求包括并发、防重、幂等等用例.
10. 代码格局标准
杰出的代码格局,能够使代码更简单阅读和了解。下面是一些常见的代码格局化主张:
- 缩进运用四个空格
- 代码块运用花括号分隔
- 每行不超过
80
个字符 - 每个办法应该按照特定的次序排列,例如:
类变量、实例变量、构造函数、公共办法、私有办法
等。
11. 接口兼容性
代码评定的时分,要重点重视是否考虑到了接口的兼容性.因为许多bug都是因为修改了对外旧接口,可是却不做兼容导致的。要害这个问题多数是比较严重的,可能直接导致体系发版失利的。新手程序员很简单犯这个错误哦~
所以,假如你的需求是在本来接口上修改,尤其这个接口是对外供给服务的话,一定要考虑接口兼容。举个例子吧,比方dubbo接口,原本是只接纳A,B参数,现在你加了一个参数C,就能够考虑这样处理:
//老接口
void oldService(A,B){
//兼容新接口,传个null替代C
newService(A,B,null);
}
//新接口,暂时不能删掉老接口,需求做兼容。
void newService(A,B,C){
...
}
12. 程序逻辑是否明晰,主次是否够清楚
代码评定的时分,要重视程序逻辑是否明晰。比方,你的一个注册接口,有参数校验、判别用户是否已经注册、刺进用户记载、发送注册成功通知等功能。假如你把一切一切功能代码塞到一个办法里面,程序逻辑就不明晰,主次不行清楚,反例如下:
public Response registerUser(String userName, String password, String email) {
if (userName == null || StringUtils.isEmpty(userName)) {
log.info("用户名不能为空!");
throw new BizException();
}
if (password == null || password.length() < 6) {
log.info("密码长度不能少于6位!");
throw new BizException();
}
if (email == null || StringUtils.isEmpty(email) || !email.contains("@")) {
log.info("邮箱格局不正确!");
throw new BizException();
}
Response response = new Response();
UserInfo userInfo = userService.queryUserInfoByUsername();
if (Objects.nonNull(userInfo)) {
response.setCode(0);
response.setMsg("注册成功");
return response;
}
UserInfo addUserInfo = new UserInfo();
addUserInfo.setUserName(userName);
addUserInfo.setPassword(password);
addUserInfo.setEmail(email);
userService.addUserInfo(addUserInfo);
MessageDo messageDo = new MessageDo();
messageDo.setUserName(userName);
messageDo.setEmail(email);
messageDo.setContent("注册成功");
messageService.sendMsg(messageDo);
response.setCode(0);
response.setMsg("注册成功");
return response;
}
其实,以上这块代码,主次不行清楚的点:参数校验就占registerUser
办法很大一部分。正例能够区分主次,抽一下小函数,如下:
public Response registerUser(String userName, String password, String email) {
//查看参数
checkRegisterParam(userName, password, email);
//查看用户是否已经存在
if (checkUserInfoExist(userName)) {
Response response = new Response();
response.setCode(0);
response.setMsg("注册成功");
return response;
}
//刺进用户
addUser(userName, password, email);
sendMsgOfRegister(userName, email);
//构造注册成功报文
Response response = new Response();
response.setCode(0);
response.setMsg("注册成功");
return response;
}
private void sendMsgOfRegister(String userName, String email) {
MessageDo messageDo = new MessageDo();
messageDo.setUserName(userName);
messageDo.setEmail(email);
messageDo.setContent("注册成功");
messageService.sendMsg(messageDo);
}
private void addUser(String userName, String password, String email) {
UserInfo addUserInfo = new UserInfo();
addUserInfo.setUserName(userName);
addUserInfo.setPassword(password);
addUserInfo.setEmail(email);
userService.addUserInfo(addUserInfo);
}
private boolean checkUserInfoExist(String userName) {
UserInfo userInfo = userService.queryUserInfoByUsername();
if (Objects.nonNull(userInfo)) {
return true;
}
return false;
}
private void checkRegisterParam(String userName, String password, String email) {
if (userName == null || StringUtils.isEmpty(userName)) {
log.info("用户名不能为空!");
throw new BizException();
}
if (password == null || password.length() < 6) {
log.info("密码长度不能少于6位!");
throw new BizException();
}
if (email == null || StringUtils.isEmpty(email) || !email.contains("@")) {
log.info("邮箱格局不正确!");
throw new BizException();
}
}
13. 安全标准
代码评定,也十分有必要评定代码是否存在安全性问题。比方:
- 输入校验:应该一直对任何来自外部的输入数据进行校验,以保证它们符合预期而且不会对体系造成伤害。校验应该包括查看数据的类型、巨细和格局。
- 防备SQL注入进犯:在运用SQL查询时,应该一直运用参数化查询或预处理句子,以防止SQL注入进犯。
- 防备跨站脚本进犯(XSS): 在Web应用程序中,应该一直对输入的HTML、JavaScript和CSS进行校验,并转义特殊字符,以防止XSS进犯。
- 防止灵敏信息走漏: 灵敏信息(如密码、密钥、会话ID等)应该在传输和存储时进行加密,以防止被未经授权的人拜访。一起,应该防止在日志、调试信息或错误消息中走漏灵敏信息。
-
防备跨站恳求假造(CSRF): 应该为一切灵敏操作(如更改密码、删去数据等)增加
CSRF
令牌,以防止未经授权的人员执行这些操作。 - 防备安全漏洞: 应该运用安全性高的算法和协议(如HTTPS、TLS)来保护灵敏数据的传输和存储,并定时对体系进行漏洞扫描和安全性审计。
其实我曾经写过一篇文章,保证数据安全的10种计划,咱们能够看看哈:保证接口数据安全的10种计划
14. 事务操控标准
- 一般引荐运用编程式事务,而不是一个注解
@Transactional
的声明式事务。因为@Transactional
有许多场景,可能导致事务不生效。 咱们能够看下我的这篇文章哈: 美团二面:spring事务不生效的15种场景 - 事务规模要清晰,数据库操作必须在事务作用规模内,假如对错数据库操作,尽量不要包括在事务内。
- 不要在事务内进行长途调用(可能导致数据不一致,比方本地成功了,可是长途办法失利了,这时分需求用分布式事务解决计划)
- 事务中防止处理太多数据,一些查询相关的操作,尽量放到事务之外(防止大事务问题)
15. 幂等处理标准
什么是幂等?
计算机科学中,幂等表示一次和屡次恳求某一个资源应该具有同样的副作用,或许说,屡次恳求所发生的影响与一次恳求执行的影响效果相同。
代码评定的时分,要重视接口是否考虑幂等。比方开户接口,屡次恳求过来的时分,需求先查一下该客户是否已经开过户,假如已经开户成功,直接回来开户成功的报文。假如还没开户,就先开户,再回来开户成功的报文。这便是幂等处理。
一般状况有这几种幂等处理计划:
- select+insert+主键/仅有索引抵触
- 直接insert + 主键/仅有索引抵触
- 状态机幂等
- 抽取防重表
- token令牌
- 失望锁
- 达观锁
- 分布式锁
幂等要求有个仅有标记,比方数据库防重表的一个事务仅有键。一起着重屡次恳求和一次恳求所发生影响是相同的。
咱们假如对接口幂等有爱好的话,能够看下我之前的这篇文章: 聊聊幂等规划
16. 中间件留意事项 (数据库,redis)
代码评定的时分,假如用数据库、Redis、RocketMq
等的中间件时,咱们需求重视这些中间件的一些留意事项哈。
比方数据库:
- 重视数据库连接池参数设置、超时参数设置是否合理
- 防止循环调用数据库操作
- 假如不分页,查询
SQL
时,假如条数不清晰,是否加了limit
约束约束 - 数据库的回来是否判空处理
- 数据库慢
SQL
是否有监控 - 表结构更新是否做兼容,存量表数据是否触及兼容问题考虑
- 索引增加是否合理
- 是否连表过多等等
比方Redis
:
- Redis的key运用是否标准
- Redis 反常捕获以及处理逻辑是否合理
- Redis连接池、超时参数设置是否合理
- Redis 是否运用了有坑的那些命令,如
hgetall、smember
- 是否可能会存在缓存穿透、缓存雪奔、缓存击穿等问题。
之前我写过一篇文章,介绍Redis运用留意点的,咱们能够看一下哈:运用Redis,你必须知道的21个留意要点
17. 留意代码坏滋味问题
了解几个常见的代码坏滋味,咱们代码评定的时分,需求重视一些哈:
- 很多重复代码(抽公用办法,规划形式)
- 办法参数过多(可封装成一个DTO对象)
- 办法过长(抽小函数)
- 判别条件太多(优化if…else)
- 不处理没用的代码(没用的import)
- 防止过度规划
18. 长途调用
长途调用是代码评定重点重视的一栏,比方:
- 不要把超时当作失利处理: 长途调用可能会失利,比方网络中断、超时等等。开发者需求留意长途调用回来的错误码,除非是清晰的失利,假如仅仅是超时等问题,不能当作失利处理!而是应该建议查询,承认是否成功,再做处理。
- 反常处理:长途调用可能会抛出反常,例如因为服务端错误或恳求格局不正确等。因而,开发人员需求保证能够捕获和处理这些反常,以防止体系崩溃或数据丢掉。
-
网络安全:因为长途调用触及网络通讯,因而开发人员需求考虑网络安全的问题,例如数据加密、认证、拜访操控等。尽可能运用安全的协议,例如
HTTPS 或 SSL/TLS
。 - 服务质量:长途调用可能会影响体系的功能和可用性。因而,开发人员需求保证服务的质量,例如防止过度运用长途调用、优化数据传输、完成负载均衡等。
- 版别兼容:因为长途调用触及不同的进程或计算机之间的通讯,因而开发人员需求留意服务端和客户端之间的版别兼容性。尽可能运用相同的接口和数据格局,防止呈现不兼容的状况。
- 尽量防止for循环长途调用: 尽量防止for循环长途调用,而应该考虑完成了批量功能的接口。