Java程序员常犯的十个错误

#1 把Array转变成ArrayList

为了把Array转变成ArrayList,程序员经常这样做:

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

Arrays.asList返回的是Arrays内部的一个静态私有的ArrayList类,他不是Java.util.ArrayList类。Java.util.Arrays.ArrayList类有set(), get(), contains()这些方法,但是他没有任何加入元素的方法,所以他的大小是固定的。要创建一个真的ArrayList,你需要这样做:

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

ArrayList的构造函数可以接收一个Collection的类型,这个类型是java.util.Arrays.ArrayList的父类。

#2 检查一个Array是否包含一个值

程序员经常这样写:

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

这个代码是可以工作的,但是我们没有必要把一个list转变成set。转变成set其实是需要消耗时间的,我们可以简化成这样:

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

或者

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

第一个的代码可读性比第二个稍微好一点。

#3 在一个循环中移除list中的一个元素

看下面这个在循环中移除元素的代码:

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]

这个代码有个眼中的问题,就是当一个元素被remove了,他的大小也变化了,同时index也发生了变化。所以,假如你想在循环中删除一个元素的话,那么使用index可能会出问题。

你也许会说,使用迭代是一个很好的在循环中删除元素的方式,你也许会觉得foreach在Java中其实就和迭代类似,其实事实上并不是这样,我们来看下面这个代码:

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

他会抛出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();
    }
}

.next()必须在.remove()之前调用。在foreach循环中,编译器会在移除元素之后调用.next(),这样就会导致ConcurrentModificationException。具体你可以看看ArrayList.iterator()的源码。

#4 Hanshtable 对比 HashMap

从算法定义的角度来说,hashtable是数据结构的名字。但是在Java中,数据结构的名字是HashMap。Hashtable和HashMap的最大的不同在于Hashtable是同步的。所以,很多时候,你并不需要使用Hashtable,HashMap就足够了。

具体可以参加这两篇文章:

HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMap

Top 10 questions about Map

#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:

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

使用原生类型是非常危险的,因为他没有类型的检测并且不安全。Set, Set<?>以及Set<Object>有非常大的差别,具体可以参见Raw type vs. Unbounded wildcard 和Type Erasure.

#6 访问权限

开发者很容易在类中使用public,这样一来,访问类的值就很方便,但是这是一个很不好的设计。好的设计就是给成员尽可能低访问权限。

参见文章: public, default, protected, and private

#7 ArrayList和LinkedList

当开发者不知道ArrayList和LinkedList之间的区别时,他们通常会使用ArrayList,因为它看起来更熟悉。然而,这两者之间却有着很大的性能差别。总得来说,当有很多插入和删除操作的时候,并且随机访问比较少的时候,LinkeList其实是更加推荐的。假如你不了解这个,推荐看一下这篇文章: ArrayList vs. LinkedList

#8 Mutable 和 Immutable

Immutable的对象有很多的优点,比如简单,安全等等。但是他需要为每一个不同的值创建一个独立的对象,而很多对象就意味着垃圾回收的消耗很大。所以在选择Mutable和Immutable之间需要一个平衡。

总得来说,mutable的对象是用来防止产生太多的中间对象。一个经典的例子就是级联很多String,假如你使用Immutable string,就会产生很多需要垃圾回收立即进行回收的中间对象,这就会浪费CPU的时间,而使用mutable的对象是一个正确的方案。(比如StringBuilder)

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

还有一些别的情况,mutable的对象是推荐的。比如把mutable对象传入一个方法,这样你可以得到多个结果,而不需要使用太多的句法hoop。另外一个例子是排序和过滤:当然你可以使用一个方法来得到原来的collection,然后返回一个排好序的,但这对大的collection来说很浪费。

#9 父类和子类的构造

这里会有一个编译错误,因为默认的父类构造函数没有定义。在java中,假如一个类没有定义构造函数,那么Java会插入一个默认的无参数的构造函数。假如一个构造函数已经在父类中定义了,比如这个例子中的Super(String s),那么编译器就不会再插入默认的无参构造函数。

子类的构造函数,不管有参或者无参,会调用父类的无参构造函数。因为编译器会在子类的构造函数中默认插入父类的无参构造函数,而我们又没有定义父类的构造函数,所以会出现编译错误。

解决这个问题的方法也很简单,

1)加入一个无参构造函数到父类中就可以了。

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

2)或者把父类中的定义的构造函数去除

3)或者在子类中显示调用super(value)。

具体可以参见:

Constructor of Super and Sub

#10 “”或者使用构造函数

string可以通过两种方法来创建:

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

他们之间有什么差别呢?

下面的例子可以给出答案:

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

想知道更多的内容,可以参见这篇文章:

Create Java String Using ” ” or Constructor?.

展望未来

这些列表是我总结了GitHub,Stack OverFlow和Google查询的出来的。我们没有任何证据去证明,他们就是真正的“十大”,但是毫无疑问,他们是非常普遍的。假如你有任何不同意的地方,可以留言。假如你知道更加通用的错误,并告诉我,我会很感激。

相关的文章:

  1. Java Remove Element from ArrayList
  2. Top 10 Methods for Java Arrays
  3. java.util.ConcurrentModificationException
  4. How to Convert Array to ArrayList in Java?

更多原创,敬请关注微信公众号,每日更新业界最新资讯:

欢迎访问个人小站:https://donggeitnote.com/2020/07/11/top-10-java-mistake/

猜你喜欢

转载自blog.csdn.net/u011960402/article/details/107552725