Java编程常见陷阱(二)
foreach 中添加和删除元素
for (String item : list) {
if (Objects.equals(item, "hello")) {
list.remove(item);
}
}
以上代码可能会抛出 ConcurrentModificationException。即便是在单线程中运行,也可能会抛出该异常,所以这个异常让人相当迷惑。
为啥呢?因为 foreach 遍历本质上也是用 Iterator 来做的,Iterator 实例在创建时会将 list 的 modCount 记录在自己的实例变量 expectedCount 中。 list 的 modCount 是记录那些会导致 list 中元素数量发生改变(add/remove等)的操作的次数。
例如:每调用一次 add 或者 remove 都会导致 modCount 增加1。上面的例子在遍历过程中,删除一个元素,list 的 modCount 增加了1,iterator 发现 list 的 modCount 和自己记录的 expectedCount 不一样,于是抛出 ConcurrentModificationException。
如果确实需要在遍历过程中修改 list,正确方法是使用 Iterator:
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (Objects.equals(item, "xx")) {
iterator.remove();
}
}
如果仅仅是为了删除元素,推荐使用 List#removeIf 方法。
ConcurrentHashMap 键值为 null 问题
ConcurrentHashMap 的 key 和 value 都不能为 null,否则抛空指针。但是 HashMap 的 key 和 value 都可以为 null。
大整数传递问题
Java 服务端在返回大整数(例如 Long 类型的整数)给前端时,应该转化为字符串返回给前端,否则可能会导致精度丢失的问题。Javascript 的 Number 类型 大致相当于 Java 中的 double 类型。
Long 类型能表示的最大值是2的63次方减一,在取值范围之内,超过2的53次方 (9007199254740992)的数值转化为 JS 的 Number 时,有些数值会有精度损失。
扩展说明,在 Long 取值范围内,任何2的指数次整数都是绝对不会存在精度损失的,所以说精度损失是一个概率问题。若浮点数尾数位与指数位空间不限,则可以精确表示任何整数,但很不幸, 双精度浮点数的尾数位只有 52 位。
return 覆盖问题
try 块中的 return 语句执行成功后,并不马上返回,而是继续执行 finally 块中的语句,如果此处存在 return 语句,则在此直接返回,无情丢弃掉 try 块中的返回点。
private int x = 0;
public int checkReturn() {
try {
// x 等于 1,此处不返回
return ++x;
} finally {
// 返回的结果是 2
return ++x;
}
}
switch 空指针问题
使用 switch 的时候,要注意判空!如果传递的值是 null,是不会进入 default 分支的,而是会抛出空指针异常,看下面的例子:
public static void s(String x) {
switch (x) {
case "a":
break;
case "b":
System.out.println("b");
break;
default:
System.out.println("default");
}
}
// 调用会抛出空指针异常
s(null);
温馨提示:反馈需要登录