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

以上這段代碼出現編譯錯誤,因為默認的父類構造函數未定義。在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");
這兩者有什么區別呢? 下面的例子可以提供一個快速的答案: