Java開發者常犯的十個錯誤

jopen 9年前發布 | 19K 次閱讀 Java

翻譯自:Top 10 Mistakes Java Developers Make

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

 

1.將數組轉換為ArrayList

為了將數組轉換為ArrayList,開發者經常會這樣做:

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

Arrays.asList()會返回一個ArrayList,但這個ArrayListArrays的私有靜態類,不是java.util.ArrayListjava.util.Arrays.ArrayListset(), get(), contains()方法,但沒有任何能增加元素的方法,所以它的大小是確定的。
為了創建一個真正的ArrayList,應該這樣做:

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

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

 

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

開發者經常這樣做:

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

這個的代碼可以工作,但沒必要首先把數組轉換為集合,把數組轉換為集合需要額外的時間。可以這樣做:

Arrays.asList(arr).contains(targetValue);
</div>

或者

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

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

 

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);
</div>

輸出如下:

[b, d]
</div>

上面的方法有一個嚴重的問題。當一個元素被移除后,列表的大小減小了,索引也就變了。所以希望利用索引在一個循環里邊刪除多個元素是做不到的。
你可能知道利用迭代器在一個循環里邊刪除元素是正確的方法,并且知道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); }</pre> </div>

它將會拋出異常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>

.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);
}
</div>

上面的代碼將會拋出異常:

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

使用原始集合類型是很危險的,因為原始集合類型跳過了泛型類型檢查,是不安全的。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;
}
</div>

在其它的一些情況下也是需要使用可變對象的,比如將可變對象作為參數傳入方法可以使你不需要使用很多語句便可以得到多個結果。另外一個例子是排序和 過濾:當然,你可以寫一個方法來接收原始的集合,并且返回一個排好序的集合,但是那樣對于大的集合就太浪費了。(來自StackOverFlow的dasblinkenlight的答案)。
Why String is Immutable?

 

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


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

public Super(){
    System.out.println("Super");
}
</div>

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

 

10.”“還是構造函數?

有兩種方式構造字符串:

//1. 利用雙引號
String x = "abc";
//2. 利用構造函數
String y = new String("abc");
</div>

 

區別在哪?
下面的例子可以給出一個快速的答案:

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>

 

關于它們內存分配的更多細節,請參考Create Java String Using ”” or Constructor?

 

將來的工作

這個列表是我根據大量的GitHub上的開源項目、Stack Overflow上的問題和一些常見的Google搜索得到的。沒有明顯示的評估證明它們是準確的前10,但它們絕對是很常見的問題。如果您不同意任一部 分,請留下您的評論。如果您能提出其它一些常見的錯誤,我將會非常感激。

翻譯自:Top 10 Mistakes Java Developers Make

來自:http://www.cnblogs.com/liushaobo/p/4375493.html

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