我們在這裏總結了Java開發人員經常犯的十大錯誤,看看你中了幾個?
1、將Array轉換爲ArrayList
要將Array轉換爲 ArrayList
,開發人員通常會這樣做:
List<String> list = Arrays.asList(arr);
Arrays.asList()
將返回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);
代碼可以工作,但不需要先將列表轉換爲set。將列表轉換爲集合需要額外的時間。它可以很簡單:
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循環中,編譯器會.next()
在刪除元素的操作後進行調用,從而導致ConcurrentModificationException
。
4、Hashtable vs HashMap
根據算法中的約定,Hashtable是數據結構的名稱。但在Java中,數據結構的名稱是HashMap
。Hashtable
和HashMap
之間的關鍵區別之一是Hashtable
是同步。所以你經常不需要Hashtable
,而HashMap
經常使用。
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>
。
6、訪問權限
開發人員經常將public用於所有的類成員變量。通過直接引用很容易獲得字段值,但這是一個非常糟糕的設計。經驗法則是爲成員提供儘可能低的訪問級別。
7、ArrayList與LinkedList
當開發人員不知道ArrayList
和LinkedList
之間的區別時,他們經常使用ArrayList
,因爲它看起來很熟悉。但是,它們之間存在巨大的性能差異。簡而言之,如果存在大量的添加/刪除操作並且沒有大量隨機訪問操作,則應該首選LinkedList
。
8、可變與不可變
不可變對象具有許多優點,例如簡單性,安全性等。但是對於每個不同的值,它需要單獨的對象,並且太多的對象可能導致高的垃圾收集成本。在可變和不可變之間進行選擇時應該保持平衡。
通常,可變對象用於避免產生太多中間對象。一個典型的例子是連接大量字符串。如果使用不可變字符串,則會產生大量符合垃圾收集條件的對象。這會浪費CPU上的時間和精力,使用可變對象正確的解決方案(例如StringBuilder
)。
String result="";
for(String s: arr){
result = result + s;
}
在需要可變對象時還有其他情況。例如,將可變對象傳遞給方法可以讓您收集多個結果,而不會跳過太多語法箍。另一個例子是排序和過濾:當然,您可以創建一個獲取原始集合的方法,並返回一個已排序的方法,但這對於較大的集合來說會變得非常浪費。
9、Super和Sub的構造函數
發生此編譯錯誤,因爲默認的超級構造函數是未定義的。在Java中,如果類沒有定義構造函數,編譯器將默認爲該類插入默認的無參數構造函數。如果構造函數是在Super類中定義的,在本例中是Super(String s),編譯器將不會插入默認的無參數構造函數。這是上面超級類的情況。
Sub類的構造函數(帶參數或無參數)將調用無參數的超級構造函數。由於編譯器嘗試將super() 插入Sub類中的2個構造函數,但未定義Super的默認構造函數,因此編譯器會報告錯誤消息。
要解決這個問題,只需1)將Super() 構造函數添加到Super類中即可
public Super(){
System.out.println("Super");
}
,或2)刪除自定義的超級構造函數,或3)添加super(value)
到子構造函數。
10、" "或構造函數?
字符串可以通過兩種方式創建:
//1. 使用雙引號
String x = "abc";
//2. 使用構造函數
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
最後
這是我基於對GitHub上的大量開源項目、Stack Overflow問題的分析。沒有評估和證明它們恰好是前10名,但絕對是非常普遍的。
如果您不贊同部分內容,請留下您的評論。如果你能指出一些更常見的其他錯誤,我將非常感謝。
“不積跬步,無以至千里”,希望未來的你能:有夢爲馬 隨處可棲!加油,少年!
關注公衆號:「Java 知己」,每天更新Java知識哦,期待你的到來!
- 發送「1024」,免費領取 30 本經典編程書籍。
- 發送「Group」,與 10 萬程序員一起進步。
- 發送「面試」,領取BATJ面試資料、面試視頻攻略。
- 發送「JavaEE 實戰」,領取《JavaEE 實戰》系列視頻教程。
- 發送「玩轉算法」,領取《玩轉算法》系列視頻教程。