Java代码审查:常见错误及改进指南

整理一下项目中不好的代码写法

20241229154732_ZA3tXTde.webp

以下是一些具有代表性的问题, 都是一些一看就明白的问题, 还有一些代码的坑, 慢慢填吧.

只针对代码, 不针对谁, 如果写的不对的对方, 你咬我啊

代码问题

还失败重试? 失败重试个啥? 直接返回了 老铁!!

代码 1 后面, 获取了 batchResult, 不应该重新赋值 code 嘛?

代码 2 为修改后

20241229154732_GNL6YDkH.webp

Intellij idea 是个好东西

20241229154732_l9cqCugh.webp
20241229154732_mOz94KIb.webp

修改为:

20241229154732_R03cnTDt.webp

catch 里面使用 printStackTrace(), 错误日志全部输出到 catalina.out, 你考虑过 catalina 的感受吗?

日志问题后面说

这是先斩后奏吗?

前面都调用了 list 的 size 方法, 后面再来判断 list 是否为 null?

这种代码我看见起码不下 10 处, 系统能稳定吗老铁?

20241229154732_poqachWZ.webp

idea 都知道的问题, 你不应该不知道

20241229154732_pk5YyTrv.webp

logger.info 输出问题:

log.info("{}", xxxx), 不要自己拼接字符串

老铁, 你可长点心吧

20241229154732_mQUneZBa.webp

老铁, 我就服你

20241229154732_CjuLa8Xz.webp

google : logger.error() 正确使用姿势

20241229154732_EXMYUxyi.webp

JDK7 之后的变化

JDK7 之 钻石语法

20241229154732_2D69JJJC.webp
20241229154732_IgcfD8YG.webp

画蛇添足

value 就是 String 类型了, 写 toString() 是为了练打字吗?

20241229154732_sSB4f0pj.webp

强迫症可能要急死

看见黄色警告了吗? 知道怎么改吗?

20241229154732_2EOmbK8I.webp

20241229154732_Yl0GTxks.webp

你就告诉我需要多宽的显示器?

老铁, 公司没给配这么宽的显示器啊… 啊, 27 寸的也看不过来啊

超过 120 列宽必须需要换行

20241229154732_Mi5SqZ8G.webp

20241229154732_d4IxIPHI.webp

超过 5 个参数, 推荐使用实体类

你还是去写 python 吧

20241229154732_D7RhYdaO.webp

知道什么叫 util 吗?

20241229154732_Q99tRBQJ.webp

Intellij IDEA 都知道会有空指针, 你还这么写?

20241229154732_uckFF7xE.webp

不能直接 return 吗? 练打字吗?

20241229154732_s6WPNs6L.webp

面试题之 String, StringBuilder, StringBuffer

JDK 5 以后 JVM 对字符串循环拼接的处理方式

20241229154732_RDocgeHo.webp

老铁, 类注释, 方法注释呢

类注释呢?

方法注释虽然有, 但是不标准啊, 老铁

没看见那么多黄色警告吗?

20241229154732_nuQzSQA9.webp

代码规范我们后面说

20241229154732_9wGwkuIH.webp

每个模块都有一个 StringUtil, 还有叫 StringUtils 的

老铁, 写之前先看看能不能复用啊, 或者复制之前, 看是不是已经有了啊.

你以为把 DDL 语句拷贝过来就不用写字段注释了吗?

老铁, 你这样骚操作我很为难啊

20241229154732_1R7jMR50.webp

在类上按 F1 看不到类注释啊

20241229154732_C6AqFxhb.webp

这样改啊

20241229154732_524c0JBV.webp

F1 直接看类注释啊, 不用跳转了啊

20241229154732_H2KhddkH.webp

F1 直接看字段注释啊, 不用再去查 DDL 了啊, 不会在蒙圈了啊

20241229154732_guzkIspg.webp

老铁, 不是中文看不懂啊

额, 这个要怪 idea 了, 居然没有默认转换

20241229154732_QtY0zZE5.webp

20241229154732_mByeIzng.webp

老铁, 把 transpartent 打开, 你就认识中文了

20241229154732_IvrhpyQY.webp

老铁, 看见黄色警告了? 如果是自己解析配置, 没有处理空白符的话, 又出 bug 了啊.. 啊.

老铁, 代码用 UTF-8 啊, 不然要乱码啊

20241229154732_iqEm4YvC.webp

全都要 UTF-8 啊, 要跟国际接轨啊

20241229154732_IY0UEuKK.webp

老铁, 0 是啥, 1 是啥, 2 又是啥啊? 脑壳都大了啊…

定义个常量啊, 常量名用拼音也比没有好啊, 老铁

20241229154732_5g7sLkNB.webp

20241229154732_isLnyliN.webp

论 MVC 架构的职责

dao 就是对表的操作, 一个 dao 对应一张表;
service 组合多个 dao 进行业务处理;
controller 做参数检查, 结果封装, 跳转页面;

20241229154732_MUIpQN0h.webp

你咋不把所有的 sql 都写在一个 xml 里面呢?

20241229154732_e35X2mlx.webp

这个也要注入? 也能注入?

😅😂🤣

20241229154732_CpUOdFER.webp

多余的 finally

redis-proxy 已经对 jedis 资源的安全释放做了处理, 不用自己在写这些冗余的代码

20241229154732_0ggLpsRN.webp

catch 里面不要做流程控制, OK?

20241229154732_mUgmSDPp.webp

改为

20241229154732_KxMkMlP9.webp

log 输出错误

日志的正确使用姿势, 你值得了解一下

推荐去搜一下 log 的正确输出方式.

20241229154732_HE2fZKMw.webp

1
log.error("访问 redis 异常", e);

做人能不能真诚一点, 写代码能不能简单一点

20241229154732_VwXgkaNB.webp

改为:

1
2
3
4
IavpResponse iavpResponse = HttpUtil.sendPost(inputParams, "gatherkey", Integer.parseInt(timeOut));
if(iavpResponse.getStatusCode() == HttpStatus.SC_OK){
return XmlConverUtil.readGatherKeyXmlOut(iavpResponse.getContent());
}

XmlConverUtil.java

1
2
3
4
5
6
public static List<GatherKeyInfo> readGatherKeyXmlOut(String xml) {
if(StringUtils.isBlank(xml)){
return null;
}
...
}

isNotEmpty 和 isNotBlank 的区别知道吗?

20241229154732_3A8gDJsY.webp

改为:

20241229154732_Aub2kvRm.webp

3 行代码搞定的事, 非要写几十行, 练打字吗?

原始代码

用于检查是否是会员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public boolean judgeMiguSuperVIP(String caller) {
boolean VIPReturn = false;
// isMiguGameMothMember 表示游戏会员状态,1 表示是包月会员,0 表示不是包月会员
String isMiguGameMothMember = "0";
try {
GameAccount gameAccount = miguGameProvider.queryUserInfo(caller);
if (gameAccount != null) {
isMiguGameMothMember = gameAccount.getMiguSupperMember();
} else {
isMiguGameMothMember = "0";
}
if ("1".equals(isMiguGameMothMember)) {
// 是咪咕超级会员
VIPReturn = true;
return VIPReturn;
} else {
// 不是咪咕超级会员
VIPReturn = false;
return VIPReturn;
}
} catch (Exception e) {
// 查询游戏账号状态异常
VIPReturn = false;
return VIPReturn;
}
}

重构 1

删除 boolean VIPReturn

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public boolean judgeMiguSuperVIP(String caller, String type) {
// isMiguGameMothMember 表示游戏会员状态,1 表示是包月会员,0 表示不是包月会员
String isMiguGameMothMember = "0";
try {
GameAccount gameAccount = miguGameProvider.queryUserInfo(caller, type);
if (gameAccount != null) {
isMiguGameMothMember = gameAccount.getMiguSupperMember();
} else {
isMiguGameMothMember = "0";
}
return "1".equals(isMiguGameMothMember);
} catch (Exception e) {
return false;
}
}

重构 2

删除 isMiguGameMothMember

1
2
3
4
5
6
7
8
9
public boolean judgeMiguSuperVIP(String caller, String type) {
// isMiguGameMothMember 表示游戏会员状态,1 表示是包月会员,0 表示不是包月会员
try {
GameAccount gameAccount = miguGameProvider.queryUserInfo(caller, type);
return gameAccount != null && "1".equals(gameAccount.getMiguSupperMember());
} catch (Exception e) {
return false;
}
}

重构 3

queryUserInfo 已经处理的下层抛出的异常, 这里不需要再处理

1
2
3
4
5
public boolean judgeMiguSuperVIP(String caller, String type) {
// isMiguGameMothMember 表示游戏会员状态,1 表示是包月会员,0 表示不是包月会员
GameAccount gameAccount = miguGameProvider.queryUserInfo(caller, type);
return gameAccount != null && "1".equals(gameAccount.getMiguSupperMember());
}

日志问题

20241229154732_TUqT6zHK.webp

老铁, 日志输出到文件要用 UTF-8 啊

不然乱码看不懂啊

20241229154732_7KCouGBj.webp

老铁, 日志输出能不能统一格式啊?

老铁, 日志输出能不能分级别啊?

老铁, 日志框架能不能统一使用一个啊?

一会 log4j, 一会 log4j2 的
做人喜新厌旧可以 (log4j2 更新, 效率更好)
但也要专一, 说好放学别走就不能走, 要跑…
说好用 log4j2 + slf4j, 就不要用 System.out.println() OK?

Maven 问题

老铁, 一个模块这么多版本啊, 怎么管理啊

maven 用来管理项目中的依赖关系的, 这个没使用 maven 有什么区别?

20241229154732_1JS1jb0y.webp

拷贝依赖的时候看没看是不是存在了?

20241229154732_uvcka7Fo.webp

老铁, 不要只晓得拷贝依赖, 不看看依赖冲突啊

20241229154732_T2zcOs2y.webp

项目结构问题

1000 个人眼里, 有 1000 个哈姆雷特, 1000 个人写代码, 就有 1000 种代码风格

20241229154732_Mz06nD0Q.webp

下一篇 musearch-project 介绍

已经重构的模块

20241229154732_6WMOlgqE.webp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.
├── musicsearch-business # 业务主模块
│ ├── business-common # 业务公共类库
│ ├── mservice-migu-game # migu-game 业务
│ └── service-meeting # meeting 服务模块
├── musicsearch-common # musicsearch 项目 公共模块
├── musicsearch-component # 组件主模块
│ ├── component-iavp # iavp 模块, 封装 iavp 相关实体和接口, 直接注入即可
│ ├── component-mybatis # mybatis 模块, 提供代码生成和 mybatis 相关功能
│ ├── component-redis # redis 模块, 注入 RedisService 即可, 提供多种模式
│ └── component-websocket # websocket 模块 netty-socket.io 封装
├── musicsearch-demo # demo 主模块
│ ├── component-mybatis-demo
│ └── component-redis-demo
├── musicsearch-dependencies-bom # 管理第三方 jar 版本和依赖关系
├── musicsearch-monitor # 暂定
├── musicsearch-parent # musicsearch 工程主模块, 管理整个功能的版本及依赖
│ └── docs # 放工程相关文档
└── musicsearch-support # 支撑模块主模块
├── musicsearch-code-generator
├── musicsearch-management-system
└── musicsearch-timer-task