Java开发中最容易出现的几个bug
作为Java开发工程师,在编写代码的过程中难免会产生各种奇思妙想的bug。有些bug很无奈,比如各种空指针,在ArrayList迭代导致数组下标越界异常等。
Java开发中最容易出现的几个bug
一:Array转换为ArrayList。
Array转换为ArrayList还会出错吗?
等一下,不要急,先看看发生了什么事。
在将数组转换成ArrayList时,通常的实践就是这样。
Listlist=Arrays.asList(arr);
Arrays.asList()将返回Arrays中,这是Arrays中的私有静态类,而非java.util.Arrays类。下面的图表显示了它。
Arrays中的ArrayList仅包含set、get、contains和其他方法,但不能像add那样使其内部结构发生变化,因此Arrays中Arrays中的ArrayList的大小是固定的。
若要创建一个能添加元素的ArrayList,可以使用以下创建方法:
ArrayListarrayList=newArrayList(Arrays.asList(arr));
由于ArrayList的构造方法就是能够接收Collection集合,因此可以使用此创建方法。
第二个:检查数组中是否有一个值。
一些程序员常常会检查数组中是否有特定的值:
Setset=newHashSet(Arrays.asList(arr));
returnset.contains(targetValue);
此代码尽管正确,但存在额外的性能损失,通常情况下,不需要把它重新转换成set,直接这样做就好了:
returnArrays.asList(arr).contains(targetValue);
或用下面的方法(穷举法,循环判断)
for(Strings:arr){
if(s.equals)(targetValue))
returntrue;
}
returnfalse;
上一段代码的可读性要好得多。
错误三:循环删除列表中的元素。
相信很多小伙伴都知道这个错误,在循环中删除元素是一种禁忌,有一阵子,我在审阅代码时,想看看其他的同伴是否犯过这样的错误。
那么,为什么不能这样做(在集合中移除元素)?看看下面的代码。
ArrayListlist=newArrayList(Arrays.asList("a","b","c");
for(inti=0;ilist.remove(i);
}
system.out.print(列表);
你们能想出这样的结果吗?难道要尝试一次尝试吗?
回答实际上是[b,d]
为何只有两个价值?那不是循环输出吗?
实际上,在列表中,当您使用外部remove时,一旦remove中的某个元素,其列表的内部结构就发生了变化,即集合总容量为4,remove1个元素后将变为3,然后再与i进行比较判断。因此,只有两个元素输出。
您也许知道如何使用迭代器是remove元素的正确方法,您可能也知道for-each和iterator的工作原理相似,因此您可以编写以下代码。
ArrayListlist=newArrayList(Arrays.asList("a","b","c");
for(Strings:list){
if(s.equals("a"))
list.remove(s);
}
那么你就用runxxx.main()方法吧,结果…ConcurrentModificationException。
为什么呢?
这是因为在ArrayList中使用外部的remove元素,导致了内部结构和游标的更改。
对于阿里开发规范,不能在for-each循环中使用remove/add操作来解释元素。
因此,每个人要用List来增加或删除元素,确保要用迭代器删除。也就是
.next()必须在.remove()之前调用。在foreach循环中,编译器在删除元素的操作之后调用.next(),从而产生ConcurrentModificationException。
差错四:Hashtable和HashMap。
这里有关于算法的规定:Hashtable是一个数据结构的名字,根据算法的惯例,但在Java中,数据结构的名称为HashMap,Hashtable和HashMap之间的一个主要差异在于Hashtable是同步的,因此很多情况下您不需要Hashtable,而用HashMap。
错误5:使用原始类型的集合。
它是一种一般方面的约束:
原始类型和无边界通配符类型在Java中可以轻松混合。对于Set,Set是原始类型,Set是无界通配符类型。
例如以下代码将原始类型List用作参数:
publicstaticvoidadd(法律法规){
list.add(o);
}
publicstaticvoidmain(String[]args){
列表=newArrayList();
add(列表,10);
Strings=list.get(0);
}
这个代码引发java.lang.ClassCastException异常,为什么?
与原始类型的集合相比更危险,因为原始类型将跳过泛型检查并且不安全,Set、Set和Set之间有很大差异,并且泛型在使用中容易导致类型清除。
众所周知,Java的泛型是伪泛型,这是因为Java在编译过程中,所有泛型信息都会被清除,正确理解泛型概念的第一个前提就是要了解类型清除。Java的泛型基本上都是在编译器这一层实现的,结果字节码中没有泛型类型信息,泛型使用时加上类型参数,编译器编译时会将其去掉,此过程变成类型擦除。
像定义List和List这样的代码,在编译之后就变成了List,JVM只看到List,而JVM所看到的类型信息对于JVM来说是不存在的。Java编译器在编译时会尽可能地找到可能发生的错误,但在运行时间内仍然不能发生的类型转换异常,而类型清理是Java的泛型和C++模板机制实现方式之间的一个重要差异。