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

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

  1. 后端开发与架构
  2. 2018.07.09
  3. 10 min read

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

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

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

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

20241229154732_VwXgkaNB.webp

改为:

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

XmlConverUtil.java

public static List<GatherKeyInfo> readGatherKeyXmlOut(String xml) {
        if(StringUtils.isBlank(xml)){
            return null;
        }
        ...
}

isNotEmpty 和 isNotBlank 的区别知道吗?

20241229154732_3A8gDJsY.webp

改为:

20241229154732_Aub2kvRm.webp

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

原始代码

用于检查是否是会员

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

    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

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 已经处理的下层抛出的异常, 这里不需要再处理

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

.
├── 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

Java 最佳实践 经验总结 踩坑记录