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

  • 2019 年 10 月 4 日
  • 筆記

我們在這裡總結了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

正是金九銀十跳槽季,為大家收集了2019年最新的面試資料,有文檔、有攻略、有影片。有需要的同學可以在公眾號【Java知己】,發送【面試】領取最新面試資料攻略!

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名,但絕對是非常普遍的。

如果您不贊同部分內容,請留下您的評論。如果你能指出一些更常見的其他錯誤,我將非常感謝。

「不積跬步,無以至千里」,希望未來的你能:有夢為馬 隨處可棲!加油,少年!