Java開發人員常犯的十個錯誤,java開發人員十個
翻譯自:Top 10 Mistakes Java Developers Make
文章列出了Java開發人員最常犯的是個錯誤。
1.將數群組轉換為ArrayList
為了將數群組轉換為ArrayList,開發人員經常會這樣做:
List<String> list = Arrays.asList(arr);
Arrays.asList()會返回一個ArrayList,但這個ArrayList是Arrays的私人靜態類,不是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.檢查一個數組是否包含某個值
開發人員經常這樣做:
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;
第一個比第二個的可讀性更好。
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 ...
使用原創組合類型是很危險的,因為原創組合類型跳過了泛型型別檢查,是不安全的。Set、Set< ? >和Set< Object >之間有很大差別。請參考Raw type vs. Unbounded wildcard和Type 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); // TrueSystem.out.println(a.equals(b)); // TrueString c = new String("abcd");String d = new String("abcd");System.out.println(c == d); // FalseSystem.out.println(c.equals(d)); // True
關於它們記憶體配置的更多細節,請參考Create Java String Using ”” or Constructor?
將來的工作
這個列表是我根據大量的GitHub上的開源項目、Stack Overflow上的問題和一些常見的Google搜尋得到的。沒有明顯示的評估證明它們是準確的前10,但它們絕對是很常見的問題。如果您不同意任一部分,請留下您的評論。如果您能提出其它一些常見的錯誤,我將會非常感激。
翻譯自:Top 10 Mistakes Java Developers Make