← 返回目录

(八) 控制语句

第 8 / 27 卷作者:guanbao yang日期:

正例:

public class LazyInitDemo { private volatile Helper helper = null;

public Helper getHelper() { if (helper == null) {

synchronized(this) { if (helper == null) {

helper = new Helper();

} }

} return helper;

} // other methods and fields...

}

17.【参考】volatile 解决多线程内存不可见问题对于一写多读,是可以解决变量同步问题,但是如果多写,同样无法解决线程安全问题。

说明:如果是 count++操作,使用如下类实现:

AtomicInteger count = new AtomicInteger();

count.addAndGet(1);

如果是 JDK8,推荐使用 LongAdder 对象,比 AtomicLong 性能更好(减少乐观锁的重试次数)。

18.【参考】HashMap 在容量不够进行 resize 时由于高并发可能出现死链,导致CPU飙升,在开发过程中注意规避此风险。

19.【参考】ThreadLocal 对象使用 static 修饰,ThreadLocal 无法解决共享对象的更新问题。

说明:这个变量是针对一个线程内所有操作共享的,所以设置为静态变量,所有此类实例共享此静态变量,也就是说在类第一次被使用时装载,只分配一块存储空间,所有此类的对象(只要是这个线程内定义的)都可以操控这个变量。

(八) 控制语句

1.【强制】在一个 switch 块内,每个 case 要么通过 continue / break / return 等来终止,要么注释说明程序将继续执行到哪一个 case 为止;在一个 switch 块内,都必须包含一个 default 语句并且放在最

后,即使它什么代码也没有。

说明:注意 break 是退出 switch 语句块,而 return 是退出方法体。

2.【强制】当switch 括号内的变量类型为 String 并且此变量为外部参数时,必须先进行 null判断。

反例:如下的代码输出是什么?

public class SwitchString { public static void main(String[] args) {

method(null);

} public static void method(String param) {

switch (param) {

// 也不是进入这里 case "null":

System.out.println("it's null");

break;

// 也不是进入这里 default:

System.out.println("default");

} }

}

3.【强制】在if / else / for / while / do 语句中必须使用大括号。

反例: if (condition) statements;

说明:即使只有一行代码,也要采用大括号的编码方式。

4.【强制】三目运算符 condition ? 表达式 1:表达式2 中,高度注意表达式 1 和2 在类型对齐时,可能抛出因自动拆箱导致的 NPE 异常。

说明:以下两种场景会触发类型对齐的拆箱操作:

1)表达式 1 或表达式 2 的值只要有一个是原始类型。

2)表达式 1 或表达式 2 的值的类型不一致,会强制拆箱升级成表示范围更大的那个类型。

反例:

Integer a = 1;

Integer b = 2;

Integer c = null;

Boolean flag = false;

// a*b 的结果是 int类型,那么 c 会强制拆箱成 int类型,抛出 NPE 异常 Integer result = (flag ? a * b : c);

5.【强制】在高并发场景中,避免使用“等于”判断作为中断或退出的条件。

说明:如果并发控制没有处理好,容易产生等值判断被“击穿”的情况,使用大于或小于的区间判断条件来代替。

反例:判断剩余奖品数量等于 0 时,终止发放奖品,但因为并发处理错误导致奖品数量瞬间变成了负数,这样的话,活动无法终止。

6.【推荐】当方法的代码总行数超过10 行时,return / throw 等中断逻辑的右大括号后需要加一个空行。

说明:这样做逻辑清晰,有利于代码阅读时重点关注。

7.【推荐】表达异常的分支时,少用 if-else 方式,这种方式可以改写成:

if (condition) { ...

return obj;

} // 接着写 else 的业务逻辑代码;

说明:如果非使用 if()...else if()...else...方式表达逻辑,避免后续代码维护困难,请勿超过 3 层。

正例:超过 3 层的 if-else的逻辑判断代码可以使用卫语句、策略模式、状态模式等来实现,其中卫语句示例如下:

public void findBoyfriend(Man man) { if (man.isUgly()) {

System.out.println("本姑娘是外貌协会的资深会员");

return;

} if (man.isBadTemper()) {

System.out.println("银河有多远,你就给我滚多远");

return;

} System.out.println("可以先交往一段时间看看");

}

8.【推荐】除常用方法(如 getXxx / isXxx)等外不要在条件判断中执行其它复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。

说明:很多 if 语句内的逻辑表达式相当复杂,与、或、取反混合运算,甚至各种方法纵深调用,理解成本非常高。如果赋值一个非常好理解的布尔变量名字,则是件令人爽心悦目的事情。

正例:

// 伪代码如下 final boolean existed = (file.open(fileName, "w") != null) && (...) || (...);

if (existed) { ...

} 反例:

public final void acquire(long arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {

selfInterrupt();

} }

9.【推荐】不要在其它表达式(尤其是条件表达式)中,插入赋值语句。

说明:赋值点类似于人体的穴位,对于代码的理解至关重要,所以赋值语句需要清晰地单独成为一行。

反例:

public Lock getLock(boolean fair) { // 算术表达式中出现赋值操作,容易忽略 count值已经被改变

threshold = (count = Integer.MAX_VALUE) - 1;

// 条件表达式中出现赋值操作,容易误认为是 sync == fair return (sync = fair) ? new FairSync() : new NonfairSync();

}

10.【推荐】循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量、获取数据库连接,进行不必要的 try-catch 操作(这个 try-catch 是否可以移至循环体外)。

11.【推荐】避免采用取反逻辑运算符。

说明:取反逻辑不利于快速理解,并且取反逻辑写法一般都存在对应的正向逻辑写法。

正例:使用 if(x < 628) 来表达 x 小于 628。

反例:使用 if(!(x >= 628)) 来表达 x 小于 628。

12.【推荐】公开接口需要进行入参保护,尤其是批量操作的接口。

反例:某业务系统,提供一个用户批量查询的接口,API 文档上有说最多查多少个,但接口实现上没做任何保护,导致调用方传了一个 1000 的用户 id 数组过来后,查询信息后,内存爆了。

全站分卷索引 · 27 卷
  1. 1一、编程规约
  2. 2(二) 常量定义
  3. 3(三) 代码格式
  4. 4(四) OOP规约
  5. 5(五) 日期时间
  6. 6(六) 集合处理
  7. 7(七) 并发处理
  8. 8(八) 控制语句
  9. 9(九) 注释规约
  10. 10(十) 前后端规约
  11. 11(十一) 其他
  12. 12二、异常日志
  13. 13(二) 异常处理
  14. 14(三) 日志规约
  15. 15三、单元测试
  16. 16四、安全规约
  17. 17五、MySQL数据库
  18. 18(二) 索引规约
  19. 19(三) SQL语句
  20. 20(四) ORM映射
  21. 21六、工程结构
  22. 22(二) 二方库依赖
  23. 23(三) 服务器
  24. 24七、设计规约
  25. 25附1:版本历史
  26. 26附2:专有名词解释
  27. 27附3:错误码列表