search
尋找貓咪~QQ 地點 桃園市桃園區 Taoyuan , Taoyuan

Java程序員最常犯的錯誤盤點之Top 10

人非聖賢,孰能無過。都說Java語言是一門簡單的編程語言,基於C++演化而來,剔除了很多C++中的複雜特性,但這並不能保證Java程序員不會犯錯。那麼對於廣大的Java程序員來說,它們最常犯的10個錯誤是什麼呢?本文通過總結出Java程序員最常犯的10大錯誤,可以有效地幫組Java後來者少走彎路,少加班,並寫出更健壯的應用程序。1. 數組轉ArrayList

為了實現把一個數組轉換成一個ArrayList,很多Java程序員會使用如下的代碼:

Arrays.asList確實會返回一個ArrayList對象,但是該類是Arrays類 中一個私有靜態內部類,而不是常見的java.util.ArrayList類。這個java.util.Arrays.ArrayList類具有 set,get,contains等方法,但是不具有任何添加或移除元素的任何方法。因為該類的大小(size)是固定的。為了創建出一個真正的ArrayList,代碼應該如下所示:

我們知道,ArrayList的構造方法可以接受一個Collection類型的對象,而我們的 java.util.Arrays.ArrayList正好也是它的一個子類。實際上,更加高效的代碼示例是:

2. 數組是否包含特定值

為了檢查數組中是否包含某個特定值,很多Java程序員會使用如下的代碼:

就功能而言,該代碼是正確無誤的,但在數組轉List,List再轉Set的過程中消耗了大量的性能。我們可以優化成如下形式:

或者,進一步優化成如下所示最高效的代碼:

3. 在迭代時移除List中的元素

首先,看一下在迭代過程中移除List中元素的代碼:

這個示例代碼的輸出結果是:

這個示例代碼中存在一個非常嚴重的錯誤。當一個元素被移除時,該List的大小(size)就會縮減,同時也改變了索引的指向。所以,在迭代的過程中使用索引,將無法從List中正確地刪除多個指定的元素。

你可能知道解決這個錯誤的方式之一是使用迭代器(iterator)。而且,你可能認為Java中的foreach語句與迭代器(iterator)是非常相似的,但實際情況並不是這樣。我們考慮一下如下的示例代碼:

這個示例代碼會拋出來一個ConcurrentModificationException。我們應該修改成如下所示:

next方法必須在remove方法之前被調用。在 foreach循環中,編譯器使得 remove方法先於next方法被調用,這就導致了ConcurrentModificationException 異常。具體細節可以查看ArrayList.iterator的源碼。

4. Hashtable vs HashMap

學習過數據結構的讀者都知道一種非常重要的數據結構叫做哈希表。在Java中,對應哈希表的的類是HashMap而不是Hashtable。HashMap與Hashtable之間的最核心區別就是:HashMap是非同步的,Hashtable是同步的。

5. 在Collection中使用原始類型

在Java中,很容易把原始類型與無限通配類型混淆。我們舉個Set相關的例子:Set就是原始類型;Set<?>就是無限通配類型。我們看一個使用在List中使用原始類型的例子:

這個示例代碼會拋出來一個異常:

在Collection使用原始類型是具有很多的類型錯誤風險的,因為原始類型沒有靜態類型檢查。實際上,Set、Set<?>和Set之間具有非常大的差異。

6. 訪問許可權

很多的Java初學者喜歡使用public來修飾類的成員。這樣可以很方便地直接訪問和存取該成員。但是,這是一種非常糟糕的編程風格,正確的設計風格應該是儘可能降低類成員的訪問許可權。

7. ArrayList vs LinkedList

很多的Java初學者不明白ArrayList與LinkedList之間的區別,所以,他們完全只用相對簡單的ArrayList,甚至不知道JDK中還存在LinkedList。但是,在某些具體場景下,這兩種List的選擇會導致程序性能的巨大差異。簡單而言:當應用場景中有很多的add/remove操作,只有少量的隨機訪問操作時,應該選擇LinkedList;在其他的場景下,考慮使用ArrayList。

8. 可變 vs 不可變

不可變的對象具有非常多的優勢,比如簡單,安全等。但是,對於每一個不同的值,都需要該類的一個對象。而且,生成很多對象帶來的問題就是可能導致頻繁的垃圾回收。所以,在選擇可變類還是不可變類時,應該綜合考慮后再做抉擇。

通常而言,可變對象可以避免創建大量的中間對象。一個非常經典的例子就是鏈接大量的短String對象為一個長的String對象。如果使用不可變String類,鏈接的過程將產生大量的,適合立即被垃圾回收的中間String對象,這將消耗大量的CPU性能和內存空間。此時,使用一個可變的StringBuilder或StringBuffer才是正確的。

除了上述情況,可變對象在其他場景下可能用於不可變對象。比如,傳遞一個可變的對象到方法內部,利用該對象可以收集多個結果,而不用在多個循環層次中跳進跳出。

9. 繼承中的構造函數

上圖中出現的兩個編譯時錯誤是因為:父類中沒有定義默認構造函數,而子類中又調用了父類的默認構造函數。在Java中,如果一個類不定義任何構造函數,編譯期將自動插入一個默認構造函數到給類中。一旦一個類定義了任何一個構造函數,編譯期就不會插入任何構造函數到類中。在上面的示例中,Super類定義了一個參數類型為String的構造函數,所以該類中只有一個構造函數,不會有默認構造函數了。

&emps;在我們的子類 Sub 中,我們定義了兩個構造函數:一個參數類型為String的構造函數,另一個為午餐的默認函數。由於它們都沒有在函數體的第一行指定調用父類的哪一個構造函數,所以它們都需要調用父類 Super 的默認構造函數。但是,父類 Super 的默認構造函數是不存在的,所以編譯器報告了這兩個錯誤信息。

10. 字元串對象的兩個構建方式

Java中的字元串對象具有兩個常見的創建方式:

它們之間的區別是什麼呢?我們再看一下如下的代碼:



熱門推薦

本文由 yidianzixun 提供 原文連結

寵物協尋 相信 終究能找到回家的路
寫了7763篇文章,獲得2次喜歡
留言回覆
回覆
精彩推薦