Java程式設計師常犯的10個錯誤
本文總結了Java程式設計師常犯的10個錯誤。
#1. 把Array轉化成ArrayList
把Array轉化成ArrayList,程式設計師經常用以下方法:
List<String> list = Arrays.asList(arr);
Arrays.asList() 實際上返回一個ArrayList,但是這個ArrayList是Arrays的一個內部私有類,而不是java.util.ArrayList類。這個私有類java.util.Arrays.ArrayList有set(), get(), contains()方法,但是不能夠新增新的元素。它的大小是固定的。如果你想要一個java.util.ArrayList,正確的方法是:
ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(arr));
java.util.ArrayList的建構函式可以接受一個集合型別。java.util.Arrays.ArrayList也繼承了集合型別,所以可以作用引數使用。
#2. 檢查陣列是否包含一個值
開發人員經常做的是:
Set<String> set = new HashSet<String>(Arrays.asList(arr)); return set.contains(targetValue);
這個程式碼是工作的,但沒有沒有效率。把列表轉換成set沒有必要,需要額外的時間。正確的方法是:
Arrays.asList(arr).contains(targetValue);
或者,一個簡單的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); }
上面的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(); } }
通過分析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. 父類和子類的建構函式
以上這段程式碼出現編譯錯誤,因為預設的父類建構函式未定義。在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
關於它們是如何分配記憶體的更多資訊,請檢視建立Java字串使用“”或建構函式?
小結
以上是我根據GitHub上的開源專案,Stack Overflow上的問題,和谷歌熱門搜尋詞所做的總結。雖然它們不是準確的top 10,但很常見的。如果你有不同的觀點或者指出更常見的錯誤,請留言。我也會更新這個列表。非常感謝。
相關文章
- Python 程式設計師經常犯的 10 個錯誤Python程式設計師
- C# 程式設計師最常犯的 10 個錯誤C#程式設計師
- Java程式設計師在寫SQL程式時候常犯的10個錯誤Java程式設計師SQL
- (網頁)Java程式設計師們最常犯的10個錯誤(轉)網頁Java程式設計師
- 程式設計師可能常犯的 6 個錯誤程式設計師
- 程式設計師準備面試時常犯的10個錯誤程式設計師面試
- 程式設計師做網頁設計常犯的8個錯誤程式設計師網頁
- Python 程式設計師最常犯的十個錯誤Python程式設計師
- PHP程式設計師最常犯的11個MySQL錯誤PHP程式設計師MySql
- 你與其他程式設計師可能常犯的 6 個錯誤程式設計師
- 程式設計師常犯的 5 個非技術性錯誤程式設計師
- 網頁設計師新手常犯的6個錯誤網頁
- Java程式設計師容易犯的10個錯誤Java程式設計師
- 程式設計師準備面試時常犯11個錯誤,切記!程式設計師面試
- 10個資料科學家常犯的程式設計錯誤(附解決方案)資料科學程式設計
- python開發者常犯的10個錯誤Python
- PHP開發者常犯的10個MySQL錯誤PHPMySql
- 每個程式設計師都會犯的10個錯誤程式設計師
- 程式設計師在打造影響力時常犯的 3 個錯程式設計師
- C# 程式設計師易犯的 10 個錯誤C#程式設計師
- Python程式設計師的10個常見錯誤Python程式設計師
- Python開發者最常犯的10個錯誤Python
- 每個程式設計師都可能犯過的10個錯誤程式設計師
- 開發者常犯的 9 個錯誤
- Java程式設計師可能犯的3個常見SQL錯誤Java程式設計師SQL
- AngularJS 開發中常犯的10個錯誤AngularJS
- Web開發人員常犯的10個錯誤Web
- [譯] 我在程式設計初級階段常犯的錯誤程式設計
- Coverity談“開發中測試”與程式設計師最常犯的編碼錯誤程式設計師
- IT人士常犯的17個職場錯誤
- 程式設計師程式設計生涯中會犯的7個錯誤程式設計師
- Java程式設計師應該知道的10個除錯技巧Java程式設計師除錯
- 程式設計師看法上的幾個典型錯誤程式設計師
- 程式設計師在頁面友好性上常犯的5種錯誤以及改正方法程式設計師
- PHP開發人員常犯的10個MysqL錯誤PHPMySql
- Java程式設計師的錯Java程式設計師
- 編寫 SQL 程式碼時常犯的九個錯誤SQL
- 十個Python程式設計師易犯的錯誤Python程式設計師