Java開發人員經常犯的10大錯誤

我們在這裏總結了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中,數據結構的名稱是HashMapHashtableHashMap之間的關鍵區別之一是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 ...

使用原始類型集合很危險,因爲原始類型集合跳過泛型類型檢查而不安全。之間存在巨大差異SetSet<?>Set<Object>

6、訪問權限

開發人員經常將public用於所有的類成員變量。通過直接引用很容易獲得字段值,但這是一個非常糟糕的設計。經驗法則是爲成員提供儘可能低的訪問級別。

7、ArrayList與LinkedList

當開發人員不知道ArrayListLinkedList之間的區別時,他們經常使用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 實戰》系列視頻教程。
  • 發送「玩轉算法」,領取《玩轉算法》系列視頻教程。

到底如何才能new出這個對象?在線等

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章