核心原则:先让错误提前暴露、减少人脑上下文负担、降低阅读时的推理成本
分为 6 大类:【分支与判断优化】【注释 & 命名优化】【表达式拆分】【抽取 / 封装】【格式与结构】【业务语义化】,每一类带反面坏代码 + 优化后好代码,结合你刚才那段 String 构造器的场景。
一、分支与判断优化(你重点问的:调整顺序、拆分判断、减少嵌套)
1. 优先 fail-fast(提前返回、提前抛异常,把异常 / 非法分支放最前面)
规则:异常、非法、边界 case 放前面;正常主逻辑放后面;尽早 return/throw,消灭 else 嵌套 ❌ 坏(嵌套深,先正常逻辑,再判断错误)
public String(char value[], int offset, int count) { if (offset >=0 && count >=0) { if (count == 0) { if(offset <= value.length){ this.value = "".value; }else{ throw new StringIndexOutOfBoundsException(offset); } }else if(offset <= value.length - count){ this.value = Arrays.copyOfRange(value, offset, offset+count); }else{ throw new StringIndexOutOfBoundsException(offset+count); } }else{ if(offset <0) throw new StringIndexOutOfBoundsException(offset); if(count <0) throw new StringIndexOutOfBoundsException(count); } }✅ 好(fail-fast,非法前置,扁平化)
public String(char value[], int offset, int count) { if (offset < 0) { throw new StringIndexOutOfBoundsException("offset不能小于0:" + offset); } if (count < 0) { throw new StringIndexOutOfBoundsException("count不能小于0:" + count); } if (count == 0) { if (offset > value.length) { throw new StringIndexOutOfBoundsException(offset); } this.value = "".value; return; } if (offset > value.length - count) { throw new StringIndexOutOfBoundsException(offset + count); } this.value = Arrays.copyOfRange(value, offset, offset + count); }2. 拆分复合判断,不要一长串 &&|| 挤在一行
❌ 坏:一堆条件揉在一起,读的时候要心算
if(offset >=0 && count >=0 && offset + count > value.length){ throw new StringIndexOutOfBoundsException(); }✅ 好:拆成独立判断,一个 if 只校验一个规则;复杂条件抽变量
boolean isOffsetValid = offset >= 0; boolean isCountValid = count >= 0; boolean isRangeOverFlow = offset > value.length - count; if (!isOffsetValid) { throw new StringIndexOutOfBoundsException("offset非法"); } if (!isCountValid) { throw new StringIndexOutOfBoundsException("count非法"); } if (isRangeOverFlow) { throw new StringIndexOutOfBoundsException("区间越界"); }3.消除 if-else 金字塔(卫语句、反转条件)
口诀:正向长逻辑,反转条件提前 return❌ 金字塔嵌套
if(student != null){ if(student.getScore() >= 60){ if(student.getAge() >12){ return "合格"; } } } return "不合格";✅ 卫语句写法
if (student == null) return "不合格"; if (student.getScore() < 60) return "不合格"; if (student.getAge() <= 12) return "不合格"; return "合格";4. 分支顺序:高频 case 放前面
业务场景:大部分请求都是正常,还是异常多?
- 校验类代码(工具、构造器):异常 / 非法前置(fail-fast)
- 业务判断:命中最多的分支放最前面,减少无效判断示例:大部分订单都是已支付 ❌ 坏:低频分支放前面
if(OrderStatus.CANCEL.equals(status)){ ... } else if(OrderStatus.REFUND.equals(status)){ ... } else if(OrderStatus.PAID.equals(status)){ ... } // 最多,放最后✅ 好
if(OrderStatus.PAID.equals(status)){ ... } // 高频优先 else if(OrderStatus.CANCEL.equals(status)){ ... } else if(OrderStatus.REFUND.equals(status)){ ... }5. 少用 else,优先 return;能用 switch 枚举就不要超长 if else
Java14+ switch 表达式可读性远强于 if-else 链条
二、注释优化(重点:注释写【为什么】,不写【做什么】)
三类注释:文档注释、难点注释、反直觉注释;禁止复述代码 ❌ 坏注释(复述代码,垃圾注释)
// 如果offset小于0,抛出异常 if(offset <0) throw new StringIndexOutOfBoundsException(offset);✅ 好注释(解释约束、风险、设计原因,解决 “为什么这么写”)
// 防int溢出:不要写成 offset + count > value.length // 当offset/count接近Integer.MAX_VALUE时,相加溢出变负数,校验失效 if (offset > value.length - count) { throw new StringIndexOutOfBoundsException(offset + count); }✅ 文档注释(对外暴露方法,说明入参、异常、边界行为)
/** * 根据字符数组的指定区间构造字符串 * @param value 源字符数组 * @param offset 起始下标,从0开始 * @param count 需要截取的字符数量 * @throws StringIndexOutOfBoundsException offset负数 / count负数 / 区间超出数组范围 */补充注释规范:
- 简单自解释代码,不加注释(多余注释反而干扰阅读)
- 业务特殊规则、历史坑、兼容逻辑、性能取舍必须写注释
- 复杂算法、数学判断、溢出风险、并发约束必须写注释
三、命名优化(性价比最高!好名字可以少写一半注释)
变量 / 方法 / 类名:表达意图,而不是表达类型 ❌ 坏命名
int a; // a是offset int b; // b是count boolean flag; List<Student> list1;✅ 好命名
int startIndex; int takeCharNum; boolean isRangeValid; List<Student> passStudentList;命名细则:
- 布尔:is/has/can/should 开头
isOverFlow、hasPermission - 方法:动词开头
validateRange、copySubCharArray - 避免缩写(除非团队共识:idx 这种通用缩写可以)
- 不要加冗余后缀:
studentObj、infoDTO尽量少
四、表达式拆分 + 抽取中间变量(把复杂计算、判断抽成语义变量)
适合:长表达式、三元嵌套、连续计算 ❌ 坏:一行塞下复杂逻辑,阅读需要大脑缓存多个值
if ((order.getPayTime() != null && order.getPayTime().isAfter(LocalDateTime.now().minusDays(7))) && order.getAmount().compareTo(new BigDecimal("100")) > 0) { sendNotice(); }✅ 好,抽变量,每个变量自带语义
LocalDateTime sevenDaysAgo = LocalDateTime.now().minusDays(7); boolean isPaidRecently = order.getPayTime() != null && order.getPayTime().isAfter(sevenDaysAgo); boolean isAmountOver100 = order.getAmount().compareTo(new BigDecimal("100")) > 0; if (isPaidRecently && isAmountOver100) { sendNotice(); }小提醒:不要过度抽取,简单判断不用拆
五、抽取函数 / 方法(单一职责:一个方法只做一件事)
把一组强相关的判断、校验逻辑抽成独立方法,方法名代替注释 拿我们 String 构造器的校验举例: ❌ 全部堆在构造器里,一大坨 ✅ 抽取校验方法
public String(char value[], int offset, int count) { validateCharRange(value, offset, count); if (count == 0) { this.value = "".value; return; } this.value = Arrays.copyOfRange(value, offset, offset + count); } /** * 校验字符数组截取区间合法性 */ private static void validateCharRange(char[] value, int offset, int count) { if (offset < 0) { throw new StringIndexOutOfBoundsException("offset非法:" + offset); } if (count < 0) { throw new StringIndexOutOfBoundsException("count非法:" + count); } if (count == 0 && offset > value.length) { throw new StringIndexOutOfBoundsException(offset); } if (offset > value.length - count) { throw new StringIndexOutOfBoundsException(offset + count); } }好处:主流程干净,一眼看到【校验 → 拷贝】;校验逻辑可以复用,单元测试好写。
延伸:
- 超大方法按阶段拆:参数校验、数据组装、调用下游、结果封装分开
- 工具类:通用校验统一放到
XXXValidator
六、数据结构 & 常量 & 枚举,消除魔法值、魔法字符串
可读性重灾区:硬编码数字字符串 ❌ 坏(魔法值,没人知道 1、2、3 代表什么)
if(status == 1){ return "成功"; }else if(status ==2){ return "退款"; }✅ 方案 1:常量
private static final int STATUS_SUCCESS = 1; private static final int STATUS_REFUND = 2;✅ 方案 2(优先)枚举,自带语义
public enum OrderStatus { SUCCESS, REFUND, CANCEL }七、格式、留白、代码排版(低成本,见效快)
- 一组逻辑之间空一行分割:校验区空一行,业务逻辑区空一行,不要全部挤成密不透风
// 校验区 if (offset < 0) { throw new StringIndexOutOfBoundsException("offset不能小于0:" + offset); } if (count < 0) { throw new StringIndexOutOfBoundsException("count不能小于0:" + count); } // 空一行分割,区分阶段 if (count == 0) { if (offset > value.length) { throw new StringIndexOutOfBoundsException(offset); } this.value = "".value; return; }- 统一缩进,不要混用 tab 空格
- 不要单行放多个语句(禁止
if(a) return; b++;挤一行,除了极简单卫语句) - 运算符前后加空格
八、避免晦涩写法,优先直白写法(牺牲一点点花哨,换可读性)
很多高级写法写起来短,但别人读着费劲 ❌ 晦涩:连续三元、复杂 stream 嵌套、位运算不加注释
String res = score >=60 ? (age>12 ? "pass":"reject") : "reject";✅ 直白卫语句,更容易看懂
if (score < 60) return "reject"; if (age <= 12) return "reject"; return "pass";取舍:团队内达成共识的简洁写法(简单 stream)可以保留;业务主链路禁止炫技
九、封装领域模型,把判断下沉到对象内部(面向对象层面)
把和一个实体绑定的判断逻辑,放到实体自身方法里,不要散落在各个 if ❌ 到处写判断
if(student.getScore() >=60 && student.getAge()>12){}✅ 下沉到实体
// Student类内 public boolean isQualified(){ return this.score >=60 && this.age >12; } // 使用处 if(student.isQualified()){}📋 总表汇总(方便你记忆、复盘)
表格
| 分类 | 手段 | 一句话说明 | |
|---|---|---|---|
| 分支判断 | fail-fast / 卫语句、反转条件 | 非法分支前置,提前 return/throw,消灭 else 嵌套 | |
| 分支判断 | 调整分支顺序 | 校验类:异常优先;业务类:高频 case 优先 | |
| 分支判断 | 拆分复合条件 | 长 && | 拆成多个独立 if,抽布尔变量 |
| 注释 | 为什么,而不是做什么 | 只注释风险、约束、历史坑、反直觉逻辑,拒绝复述代码 | |
| 命名 | 语义化命名 | 名字表达意图,布尔 is/has,方法动词开头 | |
| 表达式 | 抽取中间变量 | 复杂判断、计算抽成带名字的变量 | |
| 职责拆分 | 抽取小方法 | 一个方法只做一件事,把校验 / 组装单独抽 | |
| 常量枚举 | 消灭魔法数字字符串 | 硬编码值全部收拢到常量或枚举 | |
| 排版格式 | 留白、分段 | 逻辑块之间空行,缩进规范 | |
| 写法风格 | 拒绝炫技晦涩代码 | 优先直白写法,业务代码可读性 > 简短 | |
| OO 层面 | 行为下沉到领域对象 | 和实体相关的判断,封装进实体方法 |
✨ 记忆口诀(方便背)
命名先行,注释慎加;
非法前置,尽早返回;
长式拆变量,逻辑抽小方法;
魔法值收枚举,少写嵌套金字塔;
排版留白分段,不写花哨玄学。