1. 为什么我们需要关注日常代码质量?
我刚入行时,总觉得写出能跑通的代码就够了。直到有次线上事故,排查三天发现是一个简单的空指针异常——就因为某个方法没做判空处理。那次教训让我明白:日常代码质量不是锦上添花,而是生死攸关的事。
好的日常代码就像建筑的地基。它可能只占项目总工作量的20%,却决定了80%的稳定性。我见过太多团队在赶进度时忽略代码规范,结果后期维护成本是开发成本的3-5倍。更可怕的是,烂代码会像病毒一样传染——新人来了会模仿现有代码风格,技术债越滚越大。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从命名开始:代码即文档
2.1 变量命名的艺术
我review代码时,最头疼的就是看到满屏的a、temp、data1。好的命名应该做到:
- 见名知意:
userList比list好,expiredTime比time好 - 避免误导:别用
accountList表示非List类型的数据 - 统一风格:团队约定用
isActive就别混用activeFlag
提示:如果你纠结某个命名是否合理,试着用这个变量名写一句自然语言。比如"if user is active"比"if activeFlag is true"更符合人类思维。
2.2 方法命名的黄金法则
方法名应该是动词+宾语的结构:
- 好的例子:
calculateTotalPrice()、validateUserInput() - 反模式:
processData()(太模糊)、doSomething()(等于没说)
我有个简单测试:不看方法实现,仅通过方法名能否猜到功能?如果不行,就该重构了。记住:方法名越长不一定越差,模糊的短名称才是万恶之源。
3. 控制流:避免智商税式代码
3.1 条件语句的陷阱
新手常掉进的条件判断坑:
java复制// 反面教材
if (status == 1) { ... }
else if (status == 2) { ... }
else { ... }
问题在于:
- 魔数(Magic Number)让代码难维护
- else块可能隐藏未处理状态
改进方案:
java复制// 使用枚举定义状态
public enum OrderStatus {
UNPAID(1), PAID(2), DELIVERED(3);
private final int code;
// 构造方法等
}
// 清晰的条件判断
switch (status) {
case UNPAID -> handleUnpaid();
case PAID -> handlePaid();
default -> throw new IllegalStateException("未知状态");
}
3.2 循环中的性能炸弹
我曾优化过一个耗时10秒的报表生成,发现问题出在:
java复制for (int i = 0; i < list.size(); i++) { ... }
list.size()在每次循环都被调用!对于LinkedList简直是灾难。应该:
java复制int size = list.size(); // 提前计算
for (int i = 0; i < size; i++) { ... }
更现代的写法是用foreach或流式操作:
java复制list.forEach(item -> process(item));
4. 异常处理:别把问题扫到地毯下
4.1 最危险的代码:空的catch块
java复制try {
riskyOperation();
} catch (Exception e) {
// 什么都不做
}
这种代码比直接崩溃更可怕,因为它:
- 掩盖了真实问题
- 导致后续逻辑基于错误状态运行
- 调试时让人抓狂
至少应该:
java复制catch (SpecificException e) {
log.error("操作失败,上下文信息:{}", context, e);
throw new BusinessException("友好提示");
}
4.2 异常处理金字塔原则
我总结的经验是:
- 底层方法抛原始异常
- 中层方法转换异常类型(如DAO层SQLException转成业务异常)
- 顶层控制器处理最终异常,返回用户友好提示
就像污水处理系统:底层收集原始污水,中层过滤,顶层输出可回收水。
5. 代码复用:从CV工程师到设计师
5.1 重复代码检测技巧
我常用的找重复代码方法:
- IDE自带的Code → Inspect Code功能
- 用
CPD(Copy-Paste Detector)工具扫描 - 最原始但有效:全局搜索特定业务关键词
发现重复后,不要急着提取公共方法。先问:
- 这些代码是真的逻辑相同,还是恰好长得像?
- 合并后会不会导致参数过多?
- 未来各自变更的可能性有多大?
5.2 继承与组合的抉择
很多新手滥用继承:
java复制class UserService extends BaseService {
// 现在UserService被迫拥有所有父类方法
}
更推荐组合模式:
java复制class UserService {
private final AuditService auditService;
public void addUser(User user) {
auditService.log("添加用户");
// 业务逻辑
}
}
记住:继承是"is-a"关系,组合是"has-a"关系。当你想用继承时,十有八九该用组合。
6. 单元测试:你的代码保险绳
6.1 测试不是走过场
我见过最敷衍的测试:
java复制@Test
public void testCalculate() {
Calculator calc = new Calculator();
assertNotNull(calc); // 这能测出啥?
}
好的测试应该:
- 覆盖正常流程、边界条件、异常情况
- 每个assert一个关注点
- 测试方法名体现测试场景,如
shouldThrowExceptionWhenInputIsNegative()
6.2 测试数据构造技巧
避免这种硬编码:
java复制User user = new User();
user.setName("test");
// 其他20个字段...
用Builder模式或ObjectMother模式:
java复制User user = UserBuilder.validUser().withName("定制名称").build();
对于复杂对象,可以考虑用javafaker库生成逼真测试数据。
7. 代码评审:超越格式检查
7.1 评审清单示例
我团队的Code Review会检查:
- [ ] 方法是否超过20行
- [ ] 是否有未处理的异常
- [ ] 日志级别是否合理(ERROR不能滥用)
- [ ] 是否存在线程安全问题
- [ ] SQL是否有注入风险
- [ ] 缓存是否有雪崩/穿透可能
7.2 如何给出建设性意见
不要说:"这代码写得不好"
而要说:"如果这里用Map代替List,时间复杂度能从O(n)降到O(1),你觉得呢?"
好的CR应该是技术讨论,而不是挑错比赛。我习惯用"三明治反馈法":
- 先肯定某个设计亮点
- 指出改进建议
- 最后鼓励继续保持
8. 持续改进:从Good到Great
8.1 技术债管理
我们团队的做法:
- 发现坏味道代码就创建技术债Ticket
- 每周分配10%时间专门还债
- 重大重构前先写"探针测试"(Characterization Test)
记住:还技术债就像刷牙,每天做一点比攒着等牙疼强。
8.2 个人代码成长路线
我的进阶路径供参考:
- 第一年:让代码能工作
- 第二年:让代码易读懂
- 第三年:让代码可扩展
- 现在:让代码自解释
有个简单自测方法:半年后看自己写的代码,如果不加注释能否立即看懂?如果不行,说明还有提升空间。
