Java程序員常犯的10個錯誤

jopen 10年前發布 | 31K 次閱讀 Java

本文總結了Java程序員常犯的10個錯誤。

#1. 把Array轉化成ArrayList

把Array轉化成ArrayList,程序員經常用以下方法:

</tr> </tbody> </table> </div>

Arrays.asList() 實際上返回一個ArrayList,但是這個ArrayList是Arrays的一個內部私有類,而不是java.util.ArrayList類。這個私有類java.util.Arrays.ArrayList有set(), get(), contains()方法,但是不能夠添加新的元素。它的大小是固定的。如果你想要一個java.util.ArrayList,正確的方法是:

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

</tr> </tbody> </table> </div>

java.util.ArrayList的構造函數可以接受一個集合類型。java.util.Arrays.ArrayList也繼承了集合類型,所以可以作用參數使用。

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

開發人員經常做的是:

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

這個代碼是工作的,但沒有沒有效率。把列表轉換成set沒有必要,需要額外的時間。正確的方法是:

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

</tr> </tbody> </table> </div>

或者,一個簡單的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); }</pre>
</div>

上面的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();
}

}</pre>
</div>

通過分析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. 父類和子類的構造函數

implicit-super-constructor-is-undefined-for-default-constructor.png

以上這段代碼出現編譯錯誤,因為默認的父類構造函數未定義。在Java中,如果一個類沒有定義構造函數,編譯器會默認插入一個默認的無參數構造函數。如果程序員定義構造函數,編譯器將不插入默認的無參數構造函數。上面的代碼由于自定義了有參數的構造函數,編譯器不再插入無參數的構造函數。子類的構造函數,無論是有參數或無參數,都將調用父類無參構造函數。當子類需要父類的無參數構造函數的時候,就發生了錯誤。

解決這個問題,可以1)增加一個父類構造函數

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

,或2)刪除自定義的父類構造函數,或3)添加super(value)到子類構造函數。更多請查看父類和子類的構造函數

#10. "" 與 Constructor?

字符串可以通過兩種方式創建:

//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</pre>
</div>

關于它們是如何分配內存的更多信息,請查看創建Java字符串使用“”或構造函數

小結

以上是我根據GitHub上的開源項目,Stack Overflow上的問題,和谷歌熱門搜索詞所做的總結。雖然它們不是準確的top 10,但很常見的。如果你有不同的觀點或者指出更常見的錯誤,請留言。我也會更新這個列表。非常感謝。

英文原文: Top 10 Mistakes Java Developers Make

來自:http://www.programcreek.com/2014/01/java%E7%A8%8B%E5%BA%8F%E5%91%98%E5%B8%B8%E7%8A%AF%E7%9A%8410%E4%B8%AA%E9%94%99%E8%AF%AF/#1

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!
Arrays.asList(arr).contains(targetValue);
  • sesese色