为什么代码审查不是吹毛求疵,而是关乎质量
\n想象一下:你打开同事的拉取请求。里面有500行代码,散发着复制粘贴的味道,而且某个地方有人忘记关闭资源。你的任务不仅仅是找出错误,而是让代码变得更好。Java代码审查不是警察监管,而是共同创作。好的审查者能拯救团队免于技术债务,坏的审查者则会打击士气或遗漏漏洞。
\n在本文中,我将告诉你在Java代码审查中检查什么,以便不在琐事上浪费时间,但又不遗漏关键点。你将获得一份现成的检查清单和实践示例。
\n\n架构与设计:看本质
\n在深入细节之前,先评估整体情况。糟糕的架构无法通过格式化来修复。
\n违反SOLID原则
\n检查类是否变成了“上帝对象”。违反单一职责的示例:
\npublic class OrderService {\n public void createOrder(Order order) {\n // 验证\n // 计算折扣\n // 发送邮件\n // 日志记录\n // 保存到数据库\n }\n}\n\n这里有五个修改原因。正确的做法是将日志记录和发送功能提取到单独的服务中。
\n过度复杂
\n有时代码用复杂的方式解决简单问题。问问自己:“能不能更简单?”如果可以——就在审查中写出来。记住KISS原则。
\n\n安全性:不可忽视的漏洞
\nJava代码评审必须包括安全检查。即使项目是内部项目。
\nSQL注入
\n字符串拼接是邪恶的:
\n// 不好的做法\nString query = "SELECT * FROM users WHERE name = '" + userName + "'";\nStatement stmt = connection.createStatement();\nResultSet rs = stmt.executeQuery(query);\n\n正确的做法是使用PreparedStatement:
// 好的做法\nString query = "SELECT * FROM users WHERE name = ?";\nPreparedStatement pstmt = connection.prepareStatement(query);\npstmt.setString(1, userName);\nResultSet rs = pstmt.executeQuery();\n\n敏感数据泄露
\n检查是否记录了密码、令牌或卡号。在Lombok中使用@ToString.Exclude或显式忽略。
不安全的对象比较
\n// 不好的做法(比较引用)\nif (password == storedPassword) { ... }\n// 好的做法\nif (password.equals(storedPassword)) { ... }\n\n\n性能:瓶颈点
\n不要过早优化,但也不要忽略明显的性能问题。
\n循环中的不必要操作
\n// 不好的做法:每次调用length()\nfor (int i = 0; i < list.size(); i++) { ... }\n// 好的做法\nint size = list.size();\nfor (int i = 0; i < size; i++) { ... }\n\n对简单任务使用Stream
\n有时普通循环更易读且运行更快:
\n// 过度使用\nlist.stream().filter(x -> x > 5).collect(Collectors.toList());\n// 简单做法\nList<Integer> result = new ArrayList<>();\nfor (int x : list) {\n if (x > 5) result.add(x);\n}\n\n内存泄漏
\n检查是否忘记关闭InputStream、Connection或Session。使用try-with-resources:
try (Connection conn = getConnection();\n PreparedStatement stmt = conn.prepareStatement(sql)) {\n // 工作\n} catch (SQLException e) {\n log.error("DB error", e);\n}\n\n\n可读性与风格:代码如书
\n代码审查检查清单必须包括可读性。代码写一次,阅读数十次。
\n魔法数字和字符串
\n// 不好的做法\nif (status == 3) { ... }\n// 好的做法\nprivate static final int STATUS_COMPLETED = 3;\nif (status == STATUS_COMPLETED) { ... }\n\n