JAVA程序員最常犯的十大錯(cuò)誤

人非圣賢,孰能無(wú)過(guò)。都說(shuō)Java語(yǔ)言是一門簡(jiǎn)單的編程語(yǔ)言,基于C++演化而來(lái),剔除了很多C++中的復(fù)雜特性,但這并不能保證Java程序員不會(huì)犯錯(cuò)。那么對(duì)于廣大的Java程序員來(lái)說(shuō),它們最常犯的個(gè)錯(cuò)誤是什么呢?本文通過(guò)總結(jié)出Java程序員最常犯的大錯(cuò)誤,可以有效地幫組Java后來(lái)者少走彎路,少加班,并寫出更健壯的應(yīng)用程序。

1. 數(shù)組轉(zhuǎn)ArrayList

為了實(shí)現(xiàn)把一個(gè)數(shù)組轉(zhuǎn)換成一個(gè)ArrayList,很多Java程序員會(huì)使用如下的代碼:

Arrays.asList確實(shí)會(huì)返回一個(gè)ArrayList對(duì)象,但是該類是Arrays類 中一個(gè)私有靜態(tài)內(nèi)部類,而不是常見的java.util.ArrayList類。這個(gè)java.util.Arrays.ArrayList類具有 set(),get(),contains()等方法,但是不具有任何添加或移除元素的任何方法。因?yàn)樵擃惖拇笮?size)是固定的。為了創(chuàng)建出一個(gè)真正的ArrayList,代碼應(yīng)該如下所示:

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

2. 數(shù)組是否包含特定值

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

就功能而言,該代碼是正確無(wú)誤的,但在數(shù)組轉(zhuǎn)List,List再轉(zhuǎn)Set的過(guò)程中消耗了大量的性能。我們可以優(yōu)化成如下形式:

或者,進(jìn)一步優(yōu)化成如下所示最高效的代碼:

3. 在迭代時(shí)移除List中的元素

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

這個(gè)示例代碼的輸出結(jié)果是:

這個(gè)示例代碼中存在一個(gè)非常嚴(yán)重的錯(cuò)誤。當(dāng)一個(gè)元素被移除時(shí),該List的大小(size)就會(huì)縮減,同時(shí)也改變了索引的指向。所以,在迭代的過(guò)程中使用索引,將無(wú)法從List中正確地刪除多個(gè)指定的元素。

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

這個(gè)示例代碼會(huì)拋出來(lái)一個(gè)ConcurrentModificationException。我們應(yīng)該修改成如下所示:

next()方法必須在remove()方法之前被調(diào)用。在 foreach循環(huán)中,編譯器使得 remove()方法先于next()方法被調(diào)用,這就導(dǎo)致了ConcurrentModificationException 異常。具體細(xì)節(jié)可以查看ArrayList.iterator()的源碼。

4. Hashtable vs HashMap

學(xué)習(xí)過(guò)數(shù)據(jù)結(jié)構(gòu)的讀者都知道一種非常重要的數(shù)據(jù)結(jié)構(gòu)叫做哈希表。在Java中,對(duì)應(yīng)哈希表的的類是HashMap而不是Hashtable。HashMap與Hashtable之間的最核心區(qū)別就是:HashMap是非同步的,Hashtable是同步的。

5. 在Collection中使用原始類型

在Java中,很容易把原始類型與無(wú)限通配類型混淆。我們舉個(gè)Set相關(guān)的例子:Set就是原始類型;Set就是無(wú)限通配類型。我們看一個(gè)使用在List中使用原始類型的例子:

這個(gè)示例代碼會(huì)拋出來(lái)一個(gè)異常:

在Collection使用原始類型是具有很多的類型錯(cuò)誤風(fēng)險(xiǎn)的,因?yàn)樵碱愋蜎](méi)有靜態(tài)類型檢查。實(shí)際上,Set、Set和Set之間具有非常大的差異。

6. 訪問(wèn)權(quán)限

很多的Java初學(xué)者喜歡使用public來(lái)修飾類的成員。這樣可以很方便地直接訪問(wèn)和存取該成員。但是,這是一種非常糟糕的編程風(fēng)格,正確的設(shè)計(jì)風(fēng)格應(yīng)該是盡可能降低類成員的訪問(wèn)權(quán)限。

7. ArrayList vs LinkedList

很多的Java初學(xué)者不明白ArrayList與LinkedList之間的區(qū)別,所以,他們完全只用相對(duì)簡(jiǎn)單的ArrayList,甚至不知道JDK中還存在LinkedList。但是,在某些具體場(chǎng)景下,這兩種List的選擇會(huì)導(dǎo)致程序性能的巨大差異。簡(jiǎn)單而言:當(dāng)應(yīng)用場(chǎng)景中有很多的add/remove操作,只有少量的隨機(jī)訪問(wèn)操作時(shí),應(yīng)該選擇LinkedList;在其他的場(chǎng)景下,考慮使用ArrayList。

8. 可變 vs 不可變

不可變的對(duì)象具有非常多的優(yōu)勢(shì),比如簡(jiǎn)單,安全等。但是,對(duì)于每一個(gè)不同的值,都需要該類的一個(gè)對(duì)象。而且,生成很多對(duì)象帶來(lái)的問(wèn)題就是可能導(dǎo)致頻繁的垃圾回收。所以,在選擇可變類還是不可變類時(shí),應(yīng)該綜合考慮后再做抉擇。

通常而言,可變對(duì)象可以避免創(chuàng)建大量的中間對(duì)象。一個(gè)非常經(jīng)典的例子就是鏈接大量的短String對(duì)象為一個(gè)長(zhǎng)的String對(duì)象。如果使用不可變String類,鏈接的過(guò)程將產(chǎn)生大量的,適合立即被垃圾回收的中間String對(duì)象,這將消耗大量的CPU性能和內(nèi)存空間。此時(shí),使用一個(gè)可變的StringBuilder或StringBuffer才是正確的。

除了上述情況,可變對(duì)象在其他場(chǎng)景下可能用于不可變對(duì)象。比如,傳遞一個(gè)可變的對(duì)象到方法內(nèi)部,利用該對(duì)象可以收集多個(gè)結(jié)果,而不用在多個(gè)循環(huán)層次中跳進(jìn)跳出。

9. 繼承中的構(gòu)造函數(shù)

上圖中出現(xiàn)的兩個(gè)編譯時(shí)錯(cuò)誤是因?yàn)椋焊割愔袥](méi)有定義默認(rèn)構(gòu)造函數(shù),而子類中又調(diào)用了父類的默認(rèn)構(gòu)造函數(shù)。在Java中,如果一個(gè)類不定義任何構(gòu)造函數(shù),編譯期將自動(dòng)插入一個(gè)默認(rèn)構(gòu)造函數(shù)到給類中。一旦一個(gè)類定義了任何一個(gè)構(gòu)造函數(shù),編譯期就不會(huì)插入任何構(gòu)造函數(shù)到類中。在上面的示例中,Super類定義了一個(gè)參數(shù)類型為String的構(gòu)造函數(shù),所以該類中只有一個(gè)構(gòu)造函數(shù),不會(huì)有默認(rèn)構(gòu)造函數(shù)了。

&emps;在我們的子類 Sub 中,我們定義了兩個(gè)構(gòu)造函數(shù):一個(gè)參數(shù)類型為String的構(gòu)造函數(shù),另一個(gè)為午餐的默認(rèn)函數(shù)。由于它們都沒(méi)有在函數(shù)體的第一行指定調(diào)用父類的哪一個(gè)構(gòu)造函數(shù),所以它們都需要調(diào)用父類 Super 的默認(rèn)構(gòu)造函數(shù)。但是,父類 Super 的默認(rèn)構(gòu)造函數(shù)是不存在的,所以編譯器報(bào)告了這兩個(gè)錯(cuò)誤信息。

10. 字符串對(duì)象的兩個(gè)構(gòu)建方式

Java中的字符串對(duì)象具有兩個(gè)常見的創(chuàng)建方式:

它們之間的區(qū)別是什么呢?我們?cè)倏匆幌氯缦碌拇a:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚_t_閱讀 34,623評(píng)論 18 399
  • 前言 人生苦多,快來(lái) Kotlin ,快速學(xué)習(xí)Kotlin! 什么是Kotlin? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,669評(píng)論 9 118
  • (一)Java部分 1、列舉出JAVA中6個(gè)比較常用的包【天威誠(chéng)信面試題】 【參考答案】 java.lang;ja...
    獨(dú)云閱讀 7,241評(píng)論 0 62
  • 你可否記得 我們同躺在蔚藍(lán)海的邊岸 從早晨的第一縷陽(yáng)光開始 無(wú)盡的大海呵 留住了我們的視線 彼此的愛(ài)意只能裝進(jìn)暖暖...
    君涼閱讀 358評(píng)論 1 13
  • 老舍筆下的濟(jì)南:“城內(nèi)那么狹窄,城外又那么寬敞,山坡上臥著些小村莊,小樹莊的房頂上臥著點(diǎn)雪,對(duì),這是張小水墨畫,或...
    希拉李閱讀 2,369評(píng)論 9 23

友情鏈接更多精彩內(nèi)容