当前位置: 首页 > 后端技术 > Java

Java开发最容易写的10个bug

时间:2023-04-02 02:15:58 Java

今天谁又写了个bug,没错,他好像在说我。.....作为Java开发,我们在编写代码的过程中难免会产生各种奇葩的bug。有些BUG相当令人沮丧,比如各种空指针异常,ArrayList迭代中的删除操作导致异常,数组下标越界异常等等。如果你不小心看到同事的代码有我描述的bug,那你可以把这篇文章甩给他!!!你扔给他一篇文章,让他关注一波cxuan,就会让他一副宝藏在背后膜拜大神的样子。废话少说,言归正传。错误一:Array转ArrayListArray转ArrayList还会出错吗?这是愚蠢的。.....等等,先别着急,我们先看看是怎么回事。如果要将数组转为ArrayList,我们一般的做法是这样Listlist=Arrays.asList(arr);1Arrays.asList()会返回一个ArrayList,Arrays中的私有静态类,它不是java的.util.ArrayList类。如下图所示,Arrays内部的ArrayList只有set、get、contains等方法,而没有像add这样可以改变其内部结构的方法,所以Arrays内部的ArrayList的大小是固定的。如果想创建一个可以添加元素的ArrayList,可以使用如下创建方法:ArrayListarrayList=newArrayList(Arrays.asList(arr));1因为ArrayList的构造函数可以接收一个Collection收藏,所以这种创作方式是可行的。错误二:检查数组是否包含某个值检查数组是否包含某个值,一些程序员经常这样做:Setset=newHashSet(Arrays.asList(arr));返回集。包含(目标值);12虽然这段代码是正确的,但它有额外的性能损失。一般情况下,不需要转成set,直接转:returnArrays.asList(arr).contains(targetValue);1或者用下面的方法(穷举法,循环判断)for(Strings:arr){if(s.equals(targetValue))返回真;}返回假;上面的第一段代码比第二段更具可读性。错误三:在List中循环删除元素是一个错误相信很多朋友都知道。在循环中删除元素是大忌。有一段时间,当我审查代码时,我喜欢看看其他团队成员是否犯了这个错误。错误。毕竟,为什么不能这样做(删除集合中的元素)?并查看以下代码https://www.doc88.com/p-67216...ArrayListlist=newArrayList(Arrays.asList("a","b","c","d"));for(inti=0;ilist=newArrayList(Arrays.asList("a","b","c","d"));for(Strings:list){if(s.equals("a"))list.remove(s);}然后你填Confidentrunxxx.main()方法,结果。.....为什么会出现ConcurrentModificationException?这是因为在ArrayList中使用外部remove元素会导致其内部结构和游标发生变化。在阿里巴巴的开发规范中,也有说明不要对for-each循环内的元素进行remove/add操作。所以如果要用List来增删元素,就必须用迭代器来删除。那是https://www.doc88.com/p-67216...ArrayListlist=newArrayList(Arrays.asList("a","b","c","d"));Iteratoriter=list.iterator();while(iter.hasNext()){Strings=iter.next();if(s.equals("a")){iter.remove();}}.next()必须在.remove()之前调用。在foreach循环中,编译器在元素删除操作后调用.next(),导致ConcurrentModificationException。误区四:Hashtable和HashMap这是一种算法规范:按照算法约定,Hashtable是数据结构的名称,而在Java中,数据结构的名称是HashMap,Hashtable和HashMap的主要区别之一是那Hashtable是同步的,所以很多时候不需要Hashtable,而是用HashMap。错误5:使用原始类型的集合这是对泛型的约束:在Java中,原始类型和无限通配符类型很容易混合在一起。以Set为例,Set是原始类型,Set是无界通配符类型。例如,以下代码使用原始类型List作为参数:publicstaticvoidadd(Listlist,Objecto){list.add(o);}publicstaticvoidmain(String[]args){Listlist=newArrayList();add(list,10);Strings=list.get(0);}这段代码会抛出java.lang.ClassCastException,为什么?使用原始类型集合是危险的,因为原始类型会跳过泛型检查并且不安全。Set、Set和Set之间存在巨大差异,泛型在使用中很容易造成类型擦除。大家都知道Java的泛型是伪泛型。这是因为在Java编译期间所有通用信息都将被删除。正确理解泛型概念的第一个前提是理解类型擦除。Java的泛型基本上是在编译器层面实现的。生成的字节码不包含泛型中的类型信息。时间会被删除,这个过程称为类型擦除。如果代码中定义了List、List等类型,编译后都会变成List。JVM看到的是List,但是JVM看不到泛型附加的类型信息。Java编译器会在编译时尝试找出可能的错误,但在运行时仍然不能导致类型转换异常。类型擦除也是Java的泛型与C++模板机制实现的一个重要区别。例如,下面的示例publicclassTest{publicstaticvoidmain(String[]args){ArrayListlist1=newArrayList();list1.add("abc");ArrayListlist2=newArrayList();list2.add(123);System.out.println(list1.getClass()==list2.getClass());}}在这个例子中,我们定义了两个ArrayList数组,但是一个是ArrayList泛型,只能存储字符串;另一种是ArrayList泛型,只能存储整数。最后我们通过list1和list2对象的getClass()方法获取到它们的class信息,最后发现结果为真。它表明通用类型String和Integer已被擦除,只留下原始类型。因此,在最上面的一段代码中,Object类型加10是完全没问题的,但是将Object类型的“10”转成String类型会抛出类型转换异常。误区六:访问级别问题相信大部分开发者在设计类或者成员变量的时候都会简单粗暴的直接声明publicxxx。对于成员变量,存在一定的风险。误区七:ArrayList和LinkedList哈哈哈,ArrayList是我见过程序员使用频率最高的工具类,没有之一。当开发者不清楚ArrayList和LinkedList的区别时,往往会使用ArrayList(其实即使知道他们的区别,也不会用LinkedList,因为性能不值一提),因为ArrayList好像是比较熟悉。.....但实际上,ArrayList和LinkedList之间存在巨大的性能差异,简而言之,如果有大量的添加/删除操作而不是大量的随机访问操作,则应首选LinkedList。如果访问操作比较多,首选ArrayList,但是ArrayList不适合大量的add/remove操作。误区八:可变和不可变不可变对象有很多优点,比如简单和安全。但是,不可变对象需要为每个不同的值分配一个单独的对象,并且对象是不可重用的。如果此类对象太多,垃圾回收的成本可能会非常高。在可变和不可变之间进行选择时需要平衡。通常,可变对象用于避免创建过多的中间对象。例如,您想连接大量字符串。如果你使用一个不可变的字符串,你会有很多对象会立即被垃圾收集。这会浪费CPU时间和精力,使用可变对象是正确的解决方案(例如StringBuilder)。如下代码所示:Stringresult="";for(Strings:arr){result=result+s;}1234因此,选择可变对象还是不可变对象需要慎重选择。错误九:构造函数先看一段代码,分析为什么编译失败?出现此编译错误是因为未定义默认Super的构造函数。在Java中,如果一个类没有定义构造函数,编译器会默认为该类插入一个默认的无参构造函数。如果在Super类中定义了构造函数,在本例中为Super(Strings),编译器将不会插入默认的无参数构造函数。上面的Super类就是这种情况。要解决这个问题,只需向Super添加一个无参数构造函数。publicSuper(){System.out.println("Super");}123错误10:使用""还是构造函数考虑如下代码:Stringx="abc";Stringy=newString("abc");12上面两段代码有区别吗?也许下面这段代码会给你答案Stringa="abcd";Stringb="abcd";System.out.println(a==b);//TrueSystem.out.println(a.equals(b));//TrueStringc=newString("abcd");Stringd=newString("abcd");System.out.println(c==d);//FalseSystem.out.println(c.equals(d));//True这是一个典型的内存分配问题。后记今天给大家总结一下Java开发中常见的10个错误。虽然它们相对简单,但很容易被忽视。细节很完美。看看你是否会再次成功。如果你再做一次,嘿嘿嘿。————————————————