偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Java 開發(fā)中十個讓人頭疼的 Bug

開發(fā) 前端
作為 Java 開發(fā),我們在寫代碼的過程中難免會產(chǎn)生各種奇思妙想的 bug ,有些 bug 就挺讓人無奈的,比如說各種空指針異常,在 ArrayList 的迭代中進行刪除操作引發(fā)異常,數(shù)組下標(biāo)越界異常等。

那個誰,今天又寫 bug 了,沒錯,他說的好像就是我。。。。。。

作為 Java 開發(fā),我們在寫代碼的過程中難免會產(chǎn)生各種奇思妙想的 bug ,有些 bug 就挺讓人無奈的,比如說各種空指針異常,在 ArrayList 的迭代中進行刪除操作引發(fā)異常,數(shù)組下標(biāo)越界異常等。

如果你不小心看到同事的代碼出現(xiàn)了我所描述的這些 bug 后,那你就把我這篇文章甩給他!!!你甩給他一篇文章,并讓他關(guān)注了一波 cxuan,你會收獲他在后面像是如獲至寶并滿眼崇拜大神的目光。

廢話不多說,下面進入正題。

錯誤一:Array 轉(zhuǎn)換成 ArrayList

Array 轉(zhuǎn)換成 ArrayList 還能出錯?這是哪個笨。。。。。。

等等,你先別著急說,先來看看是怎么回事。

如果要將數(shù)組轉(zhuǎn)換為 ArrayList,我們一般的做法會是這樣

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

Arrays.asList() 將返回一個 ArrayList,它是 Arrays 中的私有靜態(tài)類,它不是 java.util.ArrayList 類。如下圖所示

Arrays 內(nèi)部的 ArrayList 只有 set、get、contains 等方法,但是沒有能夠像是 add 這種能夠使其內(nèi)部結(jié)構(gòu)進行改變的方法,所以 Arrays 內(nèi)部的 ArrayList 的大小是固定的。

如果要創(chuàng)建一個能夠添加元素的 ArrayList ,你可以使用下面這種創(chuàng)建方式:

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

因為 ArrayList 的構(gòu)造方法是可以接收一個 Collection 集合的,所以這種創(chuàng)建方式是可行的。

錯誤二:檢查數(shù)組是否包含某個值

檢查數(shù)組中是否包含某個值,部分程序員經(jīng)常會這么做:

  1. Set<String> set = new HashSet<String>(Arrays.asList(arr)); 
  2. return set.contains(targetValue); 

這段代碼雖然沒錯,但是有額外的性能損耗,正常情況下,不用將其再轉(zhuǎn)換為 set,直接這么做就好了:

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

或者使用下面這種方式(窮舉法,循環(huán)判斷)

  1. for(String s: arr){ 
  2.  if(s.equals(targetValue)) 
  3.   return true
  4. return false

上面第一段代碼比第二段更具有可讀性。

錯誤三:在 List 中循環(huán)刪除元素

這個錯誤我相信很多小伙伴都知道了,在循環(huán)中刪除元素是個禁忌,有段時間內(nèi)我在審查代碼的時候就喜歡看團隊的其他小伙伴有沒有犯這個錯誤。

說到底,為什么不能這么做(集合內(nèi)刪除元素)呢?且看下面代碼

  1. ArrayList<String> list = new ArrayList<String>(Arrays.asList("a""b""c""d")); 
  2. for (int i = 0; i < list.size(); i++) { 
  3.  list.remove(i); 
  4. System.out.println(list); 

這個輸出結(jié)果你能想到么?是不是蠢蠢欲動想試一波了?

答案其實是 [b,d]

為什么只有兩個值?我這不是循環(huán)輸出的么?

其實,在列表內(nèi)部,當(dāng)你使用外部 remove 的時候,一旦 remove 一個元素后,其列表的內(nèi)部結(jié)構(gòu)會發(fā)生改變,一開始集合總?cè)萘渴?4,remove 一個元素之后就會變?yōu)?3,然后再和 i 進行比較判斷。。。。。。所以只能輸出兩個元素。

你可能知道使用迭代器是正確的 remove 元素的方式,你還可能知道 for-each 和 iterator 這種工作方式類似,所以你寫下了如下代碼

  1. ArrayList<String> list = new ArrayList<String>(Arrays.asList("a""b""c""d")); 
  2.   
  3. for (String s : list) { 
  4.  if (s.equals("a")) 
  5.   list.remove(s); 

然后你充滿自信的 run xxx.main() 方法,結(jié)果。。。。。。ConcurrentModificationException

為啥呢?

那是因為使用 ArrayList 中外部 remove 元素,會造成其內(nèi)部結(jié)構(gòu)和游標(biāo)的改變。

在阿里開發(fā)規(guī)范上,也有不要在 for-each 循環(huán)內(nèi)對元素進行 remove/add 操作的說明。

所以大家要使用 List 進行元素的添加或者刪除操作,一定要使用迭代器進行刪除。也就是

  1. ArrayList<String> list = new ArrayList<String>(Arrays.asList("a""b""c""d")); 
  2. Iterator<String> iter = list.iterator(); 
  3. while (iter.hasNext()) { 
  4.  String s = iter.next(); 
  5.   
  6.  if (s.equals("a")) { 
  7.   iter.remove(); 
  8.  } 

.next() 必須在 .remove() 之前調(diào)用。在 foreach 循環(huán)中,編譯器會在刪除元素的操作后調(diào)用 .next(),導(dǎo)致ConcurrentModificationException。

錯誤四:Hashtable 和 HashMap

這是一條算法方面的規(guī)約:按照算法的約定,Hashtable 是數(shù)據(jù)結(jié)構(gòu)的名稱,但是在 Java 中,數(shù)據(jù)結(jié)構(gòu)的名稱是 HashMap,Hashtable 和 HashMap 的主要區(qū)別之一就是 Hashtable 是同步的,所以很多時候你不需要 Hashtable ,而是使用 HashMap。

錯誤五:使用原始類型的集合

這是一條泛型方面的約束:

在 Java 中,原始類型和無界通配符類型很容易混合在一起。以 Set 為例,Set 是原始類型,而 Set 是無界通配符類型。

比如下面使用原始類型 List 作為參數(shù)的代碼:

  1. public static void add(List list, Object o){ 
  2.  list.add(o); 
  3. public static void main(String[] args){ 
  4.  List<String> list = new ArrayList<String>(); 
  5.  add(list, 10); 
  6.  String s = list.get(0); 

這段代碼會拋出 java.lang.ClassCastException 異常,為啥呢?

使用原始類型集合是比較危險的,因為原始類型會跳過泛型檢查而且不安全,Set、Set 和 Set 存在巨大的差異,而且泛型在使用中很容易造成類型擦除。

大家都知道,Java 的泛型是偽泛型,這是因為 Java 在編譯期間,所有的泛型信息都會被擦掉,正確理解泛型概念的首要前提是理解類型擦除。Java 的泛型基本上都是在編譯器這個層次上實現(xiàn)的,在生成的字節(jié)碼中是不包含泛型中的類型信息的,使用泛型的時候加上類型參數(shù),在編譯器編譯的時候會去掉,這個過程成為類型擦除。

如在代碼中定義List和List等類型,在編譯后都會變成List,JVM 看到的只是List,而由泛型附加的類型信息對 JVM 是看不到的。Java 編譯器會在編譯時盡可能的發(fā)現(xiàn)可能出錯的地方,但是仍然無法在運行時刻出現(xiàn)的類型轉(zhuǎn)換異常的情況,類型擦除也是 Java 的泛型與 C++ 模板機制實現(xiàn)方式之間的重要區(qū)別。

比如下面這段示例:

  1. public class Test { 
  2.  
  3.     public static void main(String[] args) { 
  4.  
  5.         ArrayList<String> list1 = new ArrayList<String>(); 
  6.         list1.add("abc"); 
  7.  
  8.         ArrayList<Integer> list2 = new ArrayList<Integer>(); 
  9.         list2.add(123); 
  10.  
  11.         System.out.println(list1.getClass() == list2.getClass()); 
  12.     } 
  13.  

在這個例子中,我們定義了兩個ArrayList數(shù)組,不過一個是ArrayList泛型類型的,只能存儲字符串;一個是ArrayList泛型類型的,只能存儲整數(shù),最后,我們通過list1對象和list2對象的getClass()方法獲取他們的類的信息,最后發(fā)現(xiàn)結(jié)果為true。說明泛型類型String和Integer都被擦除掉了,只剩下原始類型。

所以,最上面那段代碼,把 10 添加到 Object 類型中是完全可以的,然而將 Object 類型的 "10" 轉(zhuǎn)換為 String 類型就會拋出類型轉(zhuǎn)換異常。

錯誤六:訪問級別問題

我相信大部分開發(fā)在設(shè)計 class 或者成員變量的時候,都會簡單粗暴的直接聲明 public xxx,這是一種糟糕的設(shè)計,聲明為 public 就很容易赤身裸體,這樣對于類或者成員變量來說,都存在一定危險性。

錯誤七:ArrayList 和 LinkedList

哈哈哈,ArrayList 是我見過程序員使用頻次最高的工具類,沒有之一。

當(dāng)開發(fā)人員不知道 ArrayList 和 LinkedList 的區(qū)別時,他們經(jīng)常使用 ArrayList(其實實際上,就算知道他們的區(qū)別,他們也不用 LinkedList,因為這點性能不值一提),因為看起來 ArrayList 更熟悉。。。。。。

但是實際上,ArrayList 和 LinkedList 存在巨大的性能差異,簡而言之,如果添加/刪除操作大量且隨機訪問操作不是很多,則應(yīng)首選 LinkedList。如果存在大量的訪問操作,那么首選 ArrayList,但是 ArrayList 不適合進行大量的添加/刪除操作。

錯誤八:可變和不可變

不可變對象有很多優(yōu)點,比如簡單、安全等。但是不可變對象需要為每個不同的值分配一個單獨的對象,對象不具備復(fù)用性,如果這類對象過多可能會導(dǎo)致垃圾回收的成本很高。在可變和不可變之間進行選擇時需要有一個平衡。

一般來說,可變對象用于避免產(chǎn)生過多的中間對象。比如你要連接大量字符串。如果你使用一個不可變的字符串,你會產(chǎn)生很多可以立即進行垃圾回收的對象。這會浪費 CPU 的時間和精力,使用可變對象是正確的解決方案(例如 StringBuilder)。如下代碼所示:

  1. String result=""
  2. for(String s: arr){ 
  3.  result = result + s; 

所以,正確選擇可變對象還是不可變對象需要慎重抉擇。

錯誤九:構(gòu)造函數(shù)

首先看一段代碼,分析為什么會編譯不通過?

發(fā)生此編譯錯誤是因為未定義默認(rèn) Super 的構(gòu)造函數(shù)。在 Java 中,如果一個類沒有定義構(gòu)造函數(shù),編譯器會默認(rèn)為該類插入一個默認(rèn)的無參數(shù)構(gòu)造函數(shù)。如果在 Super 類中定義了構(gòu)造函數(shù),在這種情況下 Super(String s),編譯器將不會插入默認(rèn)的無參數(shù)構(gòu)造函數(shù)。這就是上面 Super 類的情況。

要想解決這個問題,只需要在 Super 中添加一個無參數(shù)的構(gòu)造函數(shù)即可。

  1. public Super(){ 
  2.     System.out.println("Super"); 

錯誤十:到底是使用 "" 還是構(gòu)造函數(shù)

考慮下面代碼:

  1. String x = "abc"
  2. String y = new String("abc"); 

上面這兩段代碼有什么區(qū)別嗎?

可能下面這段代碼會給出你回答

  1. String a = "abcd"
  2. String b = "abcd"
  3. System.out.println(a == b);  // True 
  4. System.out.println(a.equals(b)); // True 
  5.   
  6. String c = new String("abcd"); 
  7. String d = new String("abcd"); 
  8. System.out.println(c == d);  // False 
  9. System.out.println(c.equals(d)); // True 

這就是一個典型的內(nèi)存分配問題。

后記

今天我給你匯總了一下 Java 開發(fā)中常見的 10 個錯誤,雖然比較簡單,但是很容易忽視的問題,細(xì)節(jié)成就完美,看看你還會不會再犯了,如果再犯,嘿嘿嘿。

本文轉(zhuǎn)載自微信公眾號「程序員cxuan」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系程序員cxuan公眾號。

 

責(zé)任編輯:武曉燕 來源: 程序員cxuan
相關(guān)推薦

2010-11-16 09:07:32

2017-10-20 12:00:36

Python全局解釋器鎖GIL

2023-04-19 09:05:44

2017-12-12 13:27:20

主板跳線USB

2022-08-22 16:03:15

軟件開發(fā)系統(tǒng)

2010-08-18 09:55:38

IE6

2019-02-01 10:05:33

開源游戲開發(fā)游戲引擎

2021-09-25 13:12:47

數(shù)據(jù)開發(fā)架構(gòu)

2023-05-24 10:24:56

代碼Python

2025-03-17 00:22:00

DeepSeek指令模型

2020-01-17 06:15:17

運維架構(gòu)技術(shù)

2017-07-14 14:52:25

MySQLAborted告警案例分析

2012-02-14 09:59:09

NoSQLCouchDB

2022-11-07 16:06:15

TypeScript開發(fā)技巧

2022-03-02 08:20:54

并發(fā)編程java后端開發(fā)

2024-12-24 08:23:31

2012-07-06 14:39:33

HTML5

2013-07-17 17:30:44

成功appapp推廣移動互聯(lián)網(wǎng)創(chuàng)業(yè)

2013-07-16 13:34:03

app成功秘訣

2019-07-03 15:01:30

戴爾
點贊
收藏

51CTO技術(shù)棧公眾號