广西十一选五玩法|广西十一选五开奖查询
  • Java 開發者最容易犯的10個錯誤

    發布:51Code 時間: 2019-01-11 10:25

  • 這 10 個錯誤是我綜合 GitHub 上的項目、StackOverflow 上的問答和 Google 搜索關鍵詞的趨勢而分析得出的。 1,將 Array 轉換成 ArrayList 時出錯 一些開發者經常用這樣的代碼將 Array 轉換成 Arr...

  • 這 10 個錯誤是我綜合 GitHub 上的項目、StackOverflow 上的問答和 Google 搜索關鍵詞的趨勢而分析得出的。

    1,將 Array 轉換成 ArrayList 時出錯

    一些開發者經常用這樣的代碼將 Array 轉換成 ArrayList

    List<String> list = Arrays.asList(arr);

    Arrays.asList() 的返回值是一個 ArrayList 類的對象,這個 ArrayList 類是 Arrays 類里的一個私有靜態類(java.util.Arrays.ArrayList),并不是 java.util.ArrayList 類。

    java.util.Arrays.ArrayList 有 set() / get() / contains() 方法,但是并不提供任何添加元素的方法,因此它的長度是固定的。如果你希望得到一個 java.util.ArrayList 類的實例,你應該這么做:

    ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(arr));

    ArrayList 的構造函數可以接受一個 Collection 實例,而 Collection 是 java.util.Arrays.ArrayList 的超類。

    2 檢查 array 里是否含有某個值時出錯

    一些開發者會這么寫:

    Set<String> set = new HashSet<String>(Arrays.asList(arr));

    return set.contains(targetValue);

    上面的代碼可以工作,但是其實沒必要把 list 轉為 set,這有些浪費時間,簡單的寫法是這樣的:

    Arrays.asList(arr).contains(targetValue);

    或者這樣的

    for(String s: arr){

        if(s.equals(targetValue))

            return true;

    }

    return false;

    這兩種寫法中,前者可讀性更好。

    3 遍歷 list 移除元素時出錯

    下面的代碼在迭代時移除了元素:

    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]

    這種代碼的問題在于,當元素被移除時,list 的長度也隨之變小了,index 也同時發生了變化。所以,如果你想要在循環中使用 index 移除多個元素,它可能不能正常工作。

    你可能認為正確的方法是使用迭代器來刪除元素,比如 foreach 循環看起來就是一個迭代器,其實這樣還是有問題。

    考慮以下代碼(代碼 1):

    ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));

     

    for (String s : list) {

        if (s.equals("a"))

            list.remove(s);

    }

    會拋出 ConcurrentModificationException 異常。

    要正確地在遍歷時刪除元素,應該這么寫(代碼 2):

    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()。

    在代碼 1 中的 foreach 循環中,編譯器會在元素的刪除操作之后調用 .next(),導致 ConcurrentModificationException 異常,如果你想深入了解,可以看看 ArrayList.iterator() 的源碼。

    4,用 Hashtable 還是用 HashMap

    一般來說,算法中的 Hashtable 是一種常見的數據結構的名字。但是在 Java 中,這種數據結構的名字卻是 HashMap,不是 Hashtable。Java 中 Hashtable 和 HashMap 的最重要的區別之一是 Hashtable 是同步的(synchronized)。因此大部分時候你不需要用 Hashtable,應該用 HashMap。

    5,直接使用 Collection 的原始類型時出錯

    在 Java 中,「原始類型」和「無限制通配符類型」很容易被搞混。舉例來說,Set 是一個原始類型,而 Set 是一個無限制通配符類型。

    下面的代碼中的 add 接受原始類型 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 是很危險的,因為原始類型沒有泛型檢查。Set / Set / Set 之間有非常大的差異,詳情可以看看《Set vs. Set》和《Java Type Erasure Mechanism》。

    6,訪問級別設置過高

    很多開發者為了省事,把類字段標記為 public,這不是個好習慣。好習慣應該是將訪問級別設置得越低越好。

    詳見《public, default, protected, and private》。

    7,ArrayList 和 LinkedList 選用錯誤

    如果不了解 ArrayList 和 LinkedList 的區別,你很容易會傾向于使用 ArrayList,因為它看起來更常見。

    但是,ArrayList 和 LinkedList 有巨大的性能差異。簡單來說,如果 add/remove 操作較多,則應該使用 LinkedList;如果隨機訪問操作較多,則應該使用 ArrayList。

    如果你想深入了解這些性能差異,可以看看《ArrayList vs. LinkedList vs. Vector》。

    8,可變還是不可變?

    不可變對象有很多好處,比如簡單、安全等。但是不可變對了要求每次改動都生成新的對象,對象一多就容易對垃圾回收造成壓力。我們應該在可變對象和不可變對象上找到一個平衡點。

    一般來說,可變對象可以避免產生太多中間對象。一個經典的例子就是連接大量字符串。如果你使用不可變字符串,你就會造出許多用完即棄的中間對象。這既浪費時間又消耗 CPU,所以這種情況下你應該使用可變對象,如 StringBuilder:

     

    String result="";

    for(String s: arr){

        result = result + s;

    }

    還有一些情況值得使用可變對象。比如你可以通過將可變對象傳入方法來收集多個結果,從而繞開語法的限制。再比如排序和過濾操作,雖然你可以返回新的被排序之后的對象,但是如果元素數量眾多,這就會浪費不少內存。

    擴展閱讀《為什么字符串是不可變的》。

    9,超類和子類的構造函數

    class Super {

      String s;

      public Super(String s){

        this.s = s;

      }

    }

     

    public class Sub extend Super{

      int x = 200;

      public Sub(String s){ // 編譯錯誤

      }

      public Sub(){  // 編譯錯誤

        System.out.println("Sub");

      }

      public static void main(String[] args){

        Sub s = new Sub();

      }

    }

    上述代碼會有編譯錯誤,因為沒有實現 Super() 構造函數。Java 中,如果一個類沒有定義構造函數,編譯器將會插入一個默認的沒有參數的構造函數。但是如果 Super 類已經有了一個構造函數 Super(String s),那么編譯器就不會插入這個默認的無參數的構造函數。這就是上述代碼的遇到的情況。

    Sub 類的兩個構造函數,一個有參數一個沒有參數,都會調用 Super 類的無參數構造函數。因為編譯器會嘗試在 Sub 類的兩個構造函數里插入 super(),由于 Super 類沒有無參數構造函數,所以編譯器就報錯了。

    解決這個問題,有三種方法:

    給 Super 類添加一個 Super() 無參數構造函數

    刪掉 Super 類里的有參數的構造函數

    在 Sub 類的構造函數里添加 super(value)

    想了解更多詳情,可以看《Constructors of Sub and Super Classes in Java?》。

    10,用 "" 還是用構造函數

    字符串可以通過兩種途徑來構造:

    // 1. 使用雙引號

    String x = "abd";

    // 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

    想了解這兩種方式生成的字符串在內存中是如何存在的,可以看看《Create Java String Using ” ” or Constructor?》

    總結

    這 10 個錯誤是我綜合 GitHub 上的項目、StackOverflow 上的問答和 Google 搜索關鍵詞的趨勢而分析得出的。它們可能并不是真正的 10 個最多的錯誤,但還是挺普遍的。

    文章來源:網絡
    上文內容不用于商業目的,如涉及知識產權問題,請權利人聯系博為峰小編(021-64471599-8103),我們將立即處理。
  • 上一篇:Java 程序員不容錯過的開發趨勢

    下一篇:一個Java字符串中到底有多少個字符?

網站導航
Copyright(C)51Code軟件開發網 2003-2019 , 滬ICP備05003035號-6
广西十一选五玩法 三肖六码3肖6码资料 美国往中国赚钱 全天北京pk10计划网页版l 宝贝全计划官方下载 动物狂欢城 像素风我的世界的手游赚钱 麻将游戏4人打真人版 精准计划软件腾讯分分彩 jdb龙王捕鱼技巧 pk10赛车计划网页版