Java 開發者最容易犯的10個錯誤——你有犯過嗎?

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

1 將 Array 轉換成 ArrayList 時出錯

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

List 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 arrayList = new ArrayList(Arrays.asList(arr));復制代碼

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

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

一些開發者會這么寫:

Setset= new HashSet(Arrays.asList(arr));returnset.contains(targetValue);復制代碼

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

Arrays.asList(arr).contains(targetValue);復制代碼

或者這樣的

for(String s: arr){if(s.equals(targetValue))returntrue;}returnfalse;復制代碼

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

3 遍歷 list 移除元素時出錯

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

ArrayList list = new ArrayList(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 list = new ArrayList(Arrays.asList("a","b","c","d"));for(String s : list) {if(s.equals("a"))list.remove(s);}復制代碼

會拋出 ConcurrentModificationException 異常。

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

ArrayList list = new ArrayList(Arrays.asList("a","b","c","d"));Iterator 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 list = new ArrayList();add(list, 10);String s = list.get(0);}復制代碼

這個代碼會在運行時才拋出異常:

Exceptioninthread"main"java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Stringat ...復制代碼

使用原始類型的 collection 是很危險的,因為原始類型沒有泛型檢查。Set / Set<?> / Set<Object>?之間有非常大的差異,詳情可以看看《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){ // 編譯錯誤? }? publicSub(){? // 編譯錯誤? ? 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);? // TrueSystem.out.println(a.equals(b)); // True String c = new String("abcd");String d = new String("abcd");System.out.println(c == d);? // FalseSystem.out.println(c.equals(d)); // True復制代碼

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

總結

這 10 個錯誤是我綜合 GitHub 上的項目、StackOverflow 上的問答和 Google 搜索關鍵詞的趨勢而分析得出的。它們可能并不是真正的 10 個最多的錯誤,但還是挺普遍的。如果你有異議,可以給我留言。如果你能告訴我其他常見的錯誤,我會非常感謝你。當然除了注意這些容易放的錯誤,還是要不斷的一直提升自己。在這里小編分享一份自己工作中提升自己的學習經驗,希望能幫助到大家。

從事java十余年,現在把架構師必須具備的一些技術總結出來一套思維導圖和錄制了一些相關視頻,分享給大家,供大家參考。

需要相關資料可以加群:810589193,點擊鏈接加入群聊【Java架構學習交流群】:https://jq.qq.com/?_wv=1027&k=5deQUBl

我把它分為六個點

1. 高性能架構

1.1. 分布式架構思維

1.2. Zookeeper分布式環境指揮官

1.3. Nginx高并發分流進階實戰

1.4. ActiveMq消息中間件

1.5. RabbitMq消息中間件

1.6. Kafka百萬級吞實戰

1.7. Memcached進階實戰

1.8. Redis高性能緩存數據庫

1.9. MongoDB進階實戰

1.10. 高性能緩存開發實戰

1.11. Mysql高性能存儲實戰

1.12. FastDFS分布式文件存儲實戰

1.13. 高并發場景分布式解決方案實戰

2. 微服務架構

2.1. 服務的前世今生

2.2. 基于分布式思想下的RPC解決方案

2.3. Dubbo應用及源碼解讀

2.4. SpringBoot

2.5. SpringCloud應用及源碼解讀

2.6. Docker虛擬化技術

3. 開源框架

3.1. spring5概述

3.2. Spring5 Framework體系結構

3.3. Spring5環境搭建

3.4. IOC源碼解析

3.5. AOP源碼解析

3.6. Spring MVC

3.7. Mybatis

4. 架構師基礎

4.1. JVM性能調優

4.2. Java程序性能優化

4.3. Tomcat

4.4. 并發編程進階

4.5. Mysql

4.6. 高性能Netty框架

4.7. Linux基礎與進階

5. 團隊協作開發

5.1. Git

5.2. Maven

5.3. Jenkins

5.4. Sonar

6. B2C商城項目

6.1. 系統設計

6.2. 用戶管理子系統

6.3. 商品管理子系統

6.4. 搜索子系統

6.5. 訂單子系統

6.6. 支付系統

6.7. 分布式調度系統

6.8. 后臺系統

不是每個人都能成為高手,但是不努力,就算有再高的天分,也白癡一個!如果你想學習 Java 工程化、高性能及分布式、高性能、深入淺出。性能調優、Spring,MyBatis,Netty 源碼分析和大數據等知識點可以來找我。

工作一到五年的程序員朋友面對目前的技術無從下手,感到很迷茫可以加群:810589193,點擊鏈接加入群聊【Java架構學習交流群】:https://jq.qq.com/?_wv=1027&k=5deQUBl里面有阿里Java高級大牛直播講解知識點,分享知識,課程內容都是各位老師多年工作經驗的梳理和總結,帶著大家全面、科學地建立自己的技術體系和技術認知!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,431評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,637評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,555評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,900評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,629評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,976評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,976評論 3 448
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,139評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,686評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,411評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,641評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,129評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,820評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,233評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,567評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,362評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,604評論 2 380