Java開發者常犯的十個錯誤

翻譯自:Top 10 Mistakes Java Developers Make java

 

文章列出了Java開發者最常犯的是個錯誤。算法

 

1.將數組轉換爲ArrayList

爲了將數組轉換爲ArrayList,開發者常常會這樣作:數組

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

Arrays.asList()會返回一個ArrayList,但這個ArrayListArrays的私有靜態類,不是java.util.ArrayListjava.util.Arrays.ArrayListset(), get(), contains()方法,但沒有任何能增長元素的方法,因此它的大小是肯定的。
爲了建立一個真正的ArrayList,應該這樣作:安全

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

ArrayList的構造函數可以接收一個Collection類型,而它也是java.util.Arrays.ArrayList的一個祖先類。markdown

 

2.檢查一個數組是否包含某個值

開發者常常這樣作:數據結構

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

這個的代碼能夠工做,但不必首先把數組轉換爲集合,把數組轉換爲集合須要額外的時間。能夠這樣作:函數

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

或者性能

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

第一個比第二個的可讀性更好。ui

 

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]

上面的方法有一個嚴重的問題。當一個元素被移除後,列表的大小減少了,索引也就變了。因此但願利用索引在一個循環裏邊刪除多個元素是作不到的。
你可能知道利用迭代器在一個循環裏邊刪除元素是正確的方法,而且知道Java的foreach循環很像一個迭代器,但事實上不是。思考下面的代碼:

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循環裏邊,編譯器會先調用.remove(),再調用.next(),從而致使異常ConcurrentModificationException。你可能想知道ArrayList.iterator()的源代碼。

 

4.HashTable vs HashMap

根據算法的約定,HashTable是這個數據結構的名字,但在Java裏邊,HashMap是這個數據結構的名字。Hashtable和 HashMap的一個關鍵性的不一樣是,HashTable是同步的,而HashMap不是。因此一般不須要HashTable,HashMap用的更多。
HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMap
Top 9 questions about Java Maps

 

5.使用原始集合類型

在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 ...

使用原始集合類型是很危險的,由於原始集合類型跳過了泛型類型檢查,是不安全的。SetSet< ? >Set< Object >之間有很大差異。請參考Raw type vs. Unbounded wildcardType Erasure

 

6.訪問級別

開發者常用Public做爲類的修飾符,這樣能夠很簡單的經過直接引用獲得值,但這是一個很是糟糕的設計。根據經驗,分配給成員的訪問級別應儘量的低。
public, default, protected, and private

 

7.ArrayList vs LinkedList

當開發者不知道ArrayList和LinkedList的區別的時候,一般會使用ArrayList,由於它看起來更熟悉。然而,二者之間有很大 的性能差別。簡單地說,當有大量的插入/刪除操做而且沒有太多的隨機訪問操做的時候,應該使用LinkedList。若是對此不太瞭解,可參考ArrayList vs. LinkedList

 

8.可變與不可變

不可變對象有許多的優勢,好比簡單,安全等等。可是對於每個不一樣的值都要有一個獨立的對象,過多的對象致使垃圾回收的高消耗。當選擇可變與不可變時應該有一個權衡。
一般狀況下,可變對象能夠用來避免產生過多的中間對象。一個經典的實例就是鏈接大量的字符串,若是使用不可變的字符串,將會產生大量的須要進行垃圾回收的對象。這會浪費CPU大量的時間,使用可變對象纔是正確的方案(好比StringBuilder)。

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

在其它的一些狀況下也是須要使用可變對象的,好比將可變對象做爲參數傳入方法可使你不須要使用不少語句即可以獲得多個結果。另一個例子是排序和 過濾:固然,你能夠寫一個方法來接收原始的集合,而且返回一個排好序的集合,可是那樣對於大的集合就太浪費了。(來自StackOverFlow的dasblinkenlight的答案)。
Why String is Immutable?

 

9.父類和子類的構造函數


由於沒有定義父類的默認構造函數,在編譯的時候會產生錯誤。在Java裏邊,若是一個類沒有定義構造函數,編譯器將會插入一個無參數的默認構造函數。若是在父類裏邊定義了一個構造函數,在此例中即Super(String s),編譯器將不會插入默認的無參數構造函數。這就是上面示例中父類的情形。
子類的構造函數,不論是沒有參數還有有參數,都會調用父類的無參構造函數。由於編譯器試圖把super()插入到子類的兩個構造函數中。可是父類默認的構造函數未定義,編譯器就會報出這個錯誤信息。
要修復這個問題,能夠簡單的經過1)在父類中添加一個Super()構造方法,就像這樣:

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

或者2)移除自定義的父類構造函數,或者3)在子類的構造函數中調用父類的super(value)。
Constructor of Super and Sub

 

10.」「仍是構造函數?

有兩種方式構造字符串:

//1. 利用雙引號
String x = "abc";
//2. 利用構造函數
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搜索獲得的。沒有明顯示的評估證實它們是準確的前10,但它們絕對是很常見的問題。若是您不一樣意任一部 分,請留下您的評論。若是您能提出其它一些常見的錯誤,我將會很是感激。

翻譯自:Top 10 Mistakes Java Developers Make

相關文章
相關標籤/搜索