黑马程序员技术交流社区

标题: Java程序员常犯的10个错误 [打印本页]

作者: 黑妞~    时间: 2014-7-2 13:41
标题: Java程序员常犯的10个错误

 本文总结了Java程序员常犯的10个错误。

  #1. 把Array转化成ArrayList

  把Array转化成ArrayList,程序员经常用以下方法:

List<String> list = Arrays.asList(arr);

  Arrays.asList() 实际上返回一个ArrayList,但是这个ArrayList是Arrays的一个内部私有类,而不是java.util.ArrayList类。这个私有类java.util.Arrays.ArrayList有set(), get(), contains()方法,但是不能够添加新的元素。它的大小是固定的。如果你想要一个java.util.ArrayList,正确的方法是:

ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(arr));

  java.util.ArrayList的构造函数可以接受一个集合类型。java.util.Arrays.ArrayList也继承了集合类型,所以可以作用参数使用。

  #2. 检查数组是否包含一个值

  开发人员经常做的是:

Set<String> set = new HashSet<String>(Arrays.asList(arr));return set.contains(targetValue);

  这个代码是工作的,但没有没有效率。把列表转换成set没有必要,需要额外的时间。正确的方法是:

Arrays.asList(arr).contains(targetValue);

  或者,一个简单的loop:

for(String s: arr){        if(s.equals(targetValue))                return true;}return false;

  第一种比第二种更具有可读性。

  #3. 在循环中删除一个列表元素

  考虑下面的代码,迭代过程中删除元素:

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));for (int i = 0; i < list.size(); i++) {        list.remove(i);}System.out.println(list);

  这段代码的输出是:

[b, d]

  这个方法有一个严重的问题。当元素被移除,该列表的大小缩减,元素索引也随之发生了变化。所以,如果你想通过使用索引来删除一个循环内的多个元素,就会导致错误的结果。

  你可能猜到可以使用iterator来删除循环中的元素。在Java中的foreach循环的工作原理就像一个iterator。 但是在这里也会发生错误。请看下面的代码:

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d")); for (String s : list) {        if (s.equals("a"))                list.remove(s);}

  上面的foreach loop代码会抛出一个异常ConcurrentModificationException. 但是下面这段代码不会。

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));Iterator<String> iter = list.iterator();while (iter.hasNext()) {        String s = iter.next();         if (s.equals("a")) {                iter.remove();        }}

  通过分析ArrayList.iterator()的原代码,我们可以发现next()方法必须要在remove()方法前被调用。在foreach loop中,编译器产生的代码会先调用next()方法,从而产生异常ConcurrentModificationException。请查看ArrayList.iterator()的原代码。

  #4. Hashtable 与 HashMap

  按照算法惯例,Hashtable是数据结构的名称。但在Java中,数据结构的名称是HashMap。Hashtable是同步的版本。所以很多时候你并不需要Hashtable,而是HashMap。 这两篇文章详细介绍了各种Map的区别和常见的问题: HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMap, Map常见10大问题,

  #5.使用原始类型Collection

  在Java中,原始类型和无界通配符类型很容易混在一起。以Set为例,Set是原始类型,而Set<?>是无界通配符类型。

  考虑下面的代码,它使用原始类型的List作为参数:

public static void add(List list, Object o){        list.add(o);}public static void main(String[] args){        List<String> list = new ArrayList<String>();        add(list, 10);        String s = list.get(0);}

  此代码将抛出一个异常:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String        at ...

  使用原始类型的Collection是危险的,因为原始类型的Collection跳过类型检查。另外值得一提的是Set, Set<?>, Set<Object>之间存在着巨大的差异。 了解更多,请查看原始类型 vs. 无界通配符类型 和 类型擦除。

  #6. 访问级别

  很多时候,开发者使用public修饰字段。这样做的好处是很容易通过直接引用来获取字段的值,但是这是一个非常糟糕的设计。经验法则是“给成员的访问级别尽可能低”。可以查看Java4种不同的访问级别public, default, protected, and private。

  #7. ArrayList 与 LinkedList

  当开发人员不知道ArrayList和LinkedList的区别的时候,他们经常使用的是ArrayList,可能因为它看起来面熟。但是ArrayList和LinkedList之间有巨大的性能差异。 简单来说如果有大量的添加/删除操作,而没有很多随机存取操作,LinkedList的应该是首选。可以查看ArrayList与LinkedList了解它们之间更多的区别。

  #8.可变性与不变性

  不可变对象有很多优点,如简单性,安全性等。但是它需要为每个不同的值创造一个单独的对象,对象太多可能会导致垃圾回收的成本高。所以可变和不可变之间进行选择时应该有一个平衡。

  一般情况下,使用可变对象,以避免产生过多的中间对象。一个经典的例子是串联了大量的字符串。如果使用的是不可变的字符串String,会产生很多可以垃圾回收的对象。这样既浪费时间也浪费CPU的运算能力,使用可变对象是正确的解决方案(如StringBuilder)。

String result="";for(String s: arr){        result = result + s;}

  另外一些情况,可变对象刚更加合适可取。例如排序(Collections.sort())。如果Collection是不可变的,排序方法每次将会返回一个新的Collection,这样会极其浪费资源。 可以看看为什么在Java中String被设计成不可变?

  #9. 父类和子类的构造函数

  以上这段代码出现编译错误,因为默认的父类构造函数未定义。在Java中,如果一个类没有定义构造函数,编译器会默认插入一个默认的无参数构造函数。如果程序员定义构造函数,编译器将不插入默认的无参数构造函数。上面的代码由于自定义了有参数的构造函数,编译器不再插入无参数的构造函数。子类的构造函数,无论是有参数或无参数,都将调用父类无参构造函数。当子类需要父类的无参数构造函数的时候,就发生了错误。

  解决这个问题,可以1)增加一个父类构造函数

public Super(){    System.out.println("Super");}

  ,或2)删除自定义的父类构造函数,或3)添加super(value)到子类构造函数。更多请查看父类和子类的构造函数。

  #10. "" 与 Constructor?

  字符串可以通过两种方式创建:

//1. use double quotesString x = "abc";//2. use constructorString y = new String("abc");

  这两者有什么区别呢? 下面的例子可以提供一个快速的答案:

String a = "abcd";String b = "abcd";System.out.println(a == b);  // TrueSystem.out.println(a.equals(b)); // True String c = new String("abcd");String d = new String("abcd");System.out.println(c == d);  // FalseSystem.out.println(c.equals(d)); // True

  关于它们是如何分配内存的更多信息,请查看创建Java字符串使用“”或构造函数?

  小结

  以上是我根据GitHub上的开源项目,Stack Overflow上的问题,和谷歌热门搜索词所做的总结。虽然它们不是准确的top 10,但很常见的。如果你有不同的观点或者指出更常见的错误,请留言。我也会更新这个列表。非常感谢。


                                                                                                 关注黑马程序员微信,获得更多干货分享


                        



作者: hero112200    时间: 2014-7-2 14:40
分享这样的技术贴,真心太好了……
作者: sports、    时间: 2014-7-5 23:29
唉.看不懂啊啊啊
作者: EarlyHeart    时间: 2014-7-6 21:00
期待这样的好贴,收藏了!
作者: ╮福尔摩斯`    时间: 2014-7-6 21:37
EarlyHeart 发表于 2014-7-6 21:00
期待这样的好贴,收藏了!

我也是,看不懂先收藏着
作者: AlanHand    时间: 2014-7-9 09:54
好像都犯完啦
作者: D-小狙    时间: 2014-7-11 09:28
看来基础还是不够扎实 ,有些就忘了
作者: 死海古卷    时间: 2014-7-21 21:32
中了不少
作者: tonglingwang    时间: 2014-7-23 20:12
有用,先收下了
作者: 闪电博尔特    时间: 2014-7-24 10:51
值得收藏
作者: 刘斌2014    时间: 2014-7-25 13:08
在复习Java 这个总结很有用
作者: liusj    时间: 2014-7-26 21:17
这样的帖子挺有用的
作者: mysouffle    时间: 2014-8-18 09:27
看来Java基础知识要很牢固才行啊!:loveliness:
作者: lhtwm1    时间: 2014-8-19 14:37
虽然现在还看不懂  但还是要顶你一下
作者: 唐王潮    时间: 2014-9-28 08:37
先收藏,再研究
作者: sunspot    时间: 2014-12-28 22:10
收藏了,谢谢分享
作者: 小小的黑马    时间: 2015-3-22 12:54
很有帮助,谢了!赞一个!!!!!!!!




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2