亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Java > 正文

Java程序員常犯的五個錯誤

2019-11-26 15:04:37
字體:
來源:轉載
供稿:網友

下面針對每一個錯誤用文字說明結合代碼詳解的方式展示給大家,具體內容如下:


1. Null 的過度使用

避免過度使用 null 值是一個最佳實踐。例如,更好的做法是讓方法返回空的 array 或者 collection 而不是 null 值,因為這樣可以防止程序拋出 NullPointerException。下面代碼片段會從另一個方法獲得一個集合:

List<String> accountIds = person.getAccountIds(); for (String accountId : accountIds) {  processAccount(accountId);}


當一個 person 沒有 account 的時候,getAccountIds() 將返回 null 值,程序就會拋出 NullPointerException 異常。因此需要加入空檢查來解決這個問題。如果將返回的 null 值替換成一個空的 list,那么 NullPointerException 也不會出現。而且,因為我們不再需要對變量 accountId 做空檢查,代碼將變得更加簡潔。

當你想避免 null 值的時候,不同場景可能采取不同做法。其中一個方法就是使用 Optional 類型,它既可以是一個空對象,也可以是一些值的封裝。
 

Optional<String> optionalString = Optional.ofNullable(nullableString); if(optionalString.isPresent()) {  System.out.println(optionalString.get());}

事實上,Java8 提供了一個更簡潔的方法:
 

Optional<String> optionalString = Optional.ofNullable(nullableString); optionalString.ifPresent(System.out::println); 

Java 是從 Java8 版本開始支持 Optional 類型,但是它在函數式編程世界早已廣為人知。在此之前,它已經在 Google Guava 中針對 Java 的早期版本被使用。

2. 忽視異常

我們經常對異常置之不理。然而,針對初學者和有經驗的 Java 程序員,最佳實踐仍是處理它們。異常拋出通常是帶有目的性的,因此在大多數情況下需要記錄引起異常的事件。別小看這件事,如果必要的話,你可以重新拋出它,在一個對話框中將錯誤信息展示給用戶或者將錯誤信息記錄在日志中。至少,為了讓其它開發者知曉前因后果,你應該解釋為什么沒有處理這個異常。
 

selfie = person.shootASelfie(); try {  selfie.show();} catch (NullPointerException e) { // Maybe, invisible man. Who cares, anyway?}

強調某個異常不重要的一個簡便途徑就是將此信息作為異常的變量名,像這樣:
 

復制代碼 代碼如下:

try { selfie.delete(); } catch (NullPointerException unimportant) {  }
 
3. 并發修改異常

這種異常發生在集合對象被修改,同時又沒有使用 iterator 對象提供的方法去更新集合中的內容。例如,這里有一個 hats 列表,并想刪除其中所有含 ear flaps 的值:
 

List<IHat> hats = new ArrayList<>(); hats.add(new Ushanka()); // that one has ear flaps hats.add(new Fedora()); hats.add(new Sombrero()); for (IHat hat : hats) {  if (hat.hasEarFlaps()) { hats.remove(hat); }}

如果運行此代碼,ConcurrentModificationException 將會被拋出,因為代碼在遍歷這個集合的同時對其進行修改。當多個進程作用于同一列表,在其中一個進程遍歷列表時,另一個進程試圖修改列表內容,同樣的異常也可能會出現。

在多線程中并發修改集合內容是非常常見的,因此需要使用并發編程中常用的方法進行處理,例如同步鎖、對于并發修改采用特殊的集合等等。Java 在單線程和多線程情況下解決這個問題有微小的差別。

收集對象并在另一個循環中刪除它們

直接的解決方案是將帶有 ear flaps 的 hats 放進一個 list,之后用另一個循環刪除它。不過這需要一個額外的集合來存放將要被刪除的 hats。
 

List<IHat> hatsToRemove = new LinkedList<>(); for (IHat hat : hats) {  if (hat.hasEarFlaps()) { hatsToRemove.add(hat); }}for (IHat hat : hatsToRemove) {  hats.remove(hat);}

使用 Iterator.remove 方法

這個方法更簡單,同時并不需要創建額外的集合:
 

Iterator<IHat> hatIterator = hats.iterator(); while (hatIterator.hasNext()) {  IHat hat = hatIterator.next(); if (hat.hasEarFlaps()) { hatIterator.remove(); }}

使用 ListIterator 的方法

當需要修改的集合實現了 List 接口時,list iterator 是非常合適的選擇。實現 ListIterator 接口的 iterator 不僅支持刪除操作,還支持 add 和 set 操作。ListIterator 接口實現了 Iterator 接口,因此這個例子看起來和 Iterator 的 remove 方法很像。唯一的區別是 hat iterator 的類型和我們獲得 iterator 的方式――使用 listIterator() 方法。下面的片段展示了如何使用  ListIterator.remove 和 ListIterator.add 方法將帶有 ear flaps 的 hat 替換成帶有sombreros 的。

IHat sombrero = new Sombrero(); ListIterator<IHat> hatIterator = hats.listIterator(); while (hatIterator.hasNext()) {  IHat hat = hatIterator.next(); if (hat.hasEarFlaps()) { hatIterator.remove(); hatIterator.add(sombrero); }}

使用 ListIterator,調用 remove 和 add 方法可替換為只調用一個 set 方法:
 

IHat sombrero = new Sombrero(); ListIterator<IHat> hatIterator = hats.listIterator(); while (hatIterator.hasNext()) {  IHat hat = hatIterator.next(); if (hat.hasEarFlaps()) { hatIterator.set(sombrero); // set instead of remove and add }}

使用Java 8中的 stream 方法

在 Java8 中,開發人員可以將一個 collection 轉換為 stream,并且根據一些條件過濾 stream。這個例子講述了 stream api 是如何過濾 hats 和避免 ConcurrentModificationException 。hats = hats.stream().filter((hat -> !hat.hasEarFlaps()))

復制代碼 代碼如下:

 .collect(Collectors.toCollection(ArrayList::new));
Collectors.toCollection 方法將會創建一個新的 ArrayList,它負責存放被過濾掉的 hats 值。如果過濾條件過濾掉了大量條目,這里將會產生一個很大的 ArrayList。因此,需要謹慎使用。

使用 Java 8 中的 List.removeIf 方法

可以使用 Java 8 中另一個更簡潔明了的方法―― removeIf 方法:
 

復制代碼 代碼如下:

 hats.removeIf(IHat::hasEarFlaps);

在底層,它使用 Iterator.remove 來完成這個操作。

使用特殊的集合

如果在一開始就決定使用 CopyOnWriteArrayList 而不是 ArrayList ,那就不會出現問題。因為  CopyOnWriteArrayList 提供了修改的方法(例如 set,add,remove),它不會去改變原始集合數組,而是創建了一個新的修改版本。這就允許遍歷原來版本集合的同時進行修改,從而不會拋出  ConcurrentModificationException 異常。這種集合的缺點也非常明顯――針對每次修改都產生一個新的集合。

還有其他適用于不同場景的集合,比如 CopyOnWriteSet 和 ConcurrentHashMap 。

關于另一個可能可能在并發修改集合時產生的錯誤是,從一個 collection 創建了一個 stream,在遍歷 stream 的時候,同時修改后端的 collection。針對 stream 的一般準則是,在查詢 stream 的時候,避免修改后端的 collection。接下來的例子將展示如何正確地處理 stream:
 

List<IHat> filteredHats = hats.stream().peek(hat -> {  if (hat.hasEarFlaps()) { hats.remove(hat); }}).collect(Collectors.toCollection(ArrayList::new));

peek 方法收集所有的元素,并對每一個元素執行既定動作。在這里,動作即為嘗試從一個基礎列表中刪除數據,這顯然是錯誤的。為避免這樣的操作,可以嘗試一些上面講解的方法。

4. 違約

有時候,為了更好地協作,由標準庫或者第三方提供的代碼必須遵守共同的依賴準則。例如,必須遵守 hashCode 和 equals 的共同約定,從而保證 Java 集合框架中的一系列集合類和其它使用 hashCode 和 equals 方法的類能夠正常工作。不遵守約定并不會產生 exception 或者破壞代碼編譯之類的錯誤;它很陰險,因為它隨時可能在毫無危險提示的情況下更改應用程序行為。

錯誤代碼可能潛入生產環境,從而造成一大堆不良影響。這包括較差的 UI 體驗、錯誤的數據報告、較差的應用性能、數據丟失或者更多。慶幸的是,這些災難性的錯誤不會經常發生。在之前已經提及了 hashCode 和equals 約定,它出現的場景可能是:集合依賴于將對象進行哈?;蛘弑容^,就像 HashMap 和 HashSet。簡單來說,這個約定有兩個準則:

如果兩個對象相等,那么 hash code 必須相等。
如果兩個對象有相同的 hash code,那么它們可能相等也可能不相等。
破壞約定的第一條準則,當你試圖從一個 hashmap 中檢索數據的時候將會導致錯誤。第二個準則意味著擁有相同 hash code 的對象不一定相等。

下面看一下破壞第一條準則的后果:

public static class Boat {  private String name; Boat(String name) { this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Boat boat = (Boat) o; return !(name != null ? !name.equals(boat.name) : boat.name != null); } @Override public int hashCode() { return (int) (Math.random() * 5000); }}

正如你所見,Boat 類重寫了 equals 和 hashCode 方法。然而,它破壞了約定,因為 hashCode 針對每次調用的相同對象返回了隨機值。下面的代碼很可能在 hashset 中找不到一個名為 Enterprise 的boat,盡管事實上我們提前加入了這種類型的 boat:
 

public static void main(String[] args) {  Set<Boat> boats = new HashSet<>(); boats.add(new Boat("Enterprise")); System.out.printf("We have a boat named 'Enterprise' : %b/n", boats.contains(new Boat("Enterprise")));}

另一個約定的例子是 finalize 方法。這里是官方 Java 文檔關于它功能描述的引用:

finalize 的常規約定是:當 JavaTM 虛擬機確定任何線程都無法再通過任何方式訪問指定對象時,這個方法會被調用,此后這個對象只能在某個其他(準備終止的)對象或類終結時被作為某個行為的結果。 finalize 方法有多個功能,其中包括再次使此對象對其他線程可用;不過 finalize 的主要目的是在不可撤消地丟棄對象之前執行清除操作。例如,表示輸入/輸出連接對象的 finalize 方法可執行顯式 I/O 事務,以便在永久丟棄對象之前中斷連接。

你可以決定在諸如文件處理器中使用 finalize 方法來釋放資源,但是這種用法是很糟糕的。由于它是在垃圾回收期間被調用的,而 GC 的時間并不確定,因此 finalize 被調用的時間將無法保證。

5. 使用原始類型而不是參數化的

根據 Java 文檔描述:原始類型要么是非參數化的,要么是類 R 的(同時也是非繼承 R 父類或者父接口的)非靜態成員。在 Java 泛型被引入之前,并沒有原始類型的替代類型。Java 從1.5版本開始支持泛型編程,毫無疑問這是一個重要的功能提升。然而,由于向后兼容的原因,這里存在一個陷阱可能會破壞整個類型系統。著眼下例:
 

List listOfNumbers = new ArrayList(); listOfNumbers.add(10); listOfNumbers.add("Twenty"); listOfNumbers.forEach(n -> System.out.println((int) n * 2)); 

這是一個由數字組成的列表被定義為原始的 ArrayList。由于它并沒有指定類型參數,因此可以給它添加任何對象。但是最后一行將其包含的元素映射為 int 類型并乘以 2,打印出翻倍之后的數據到標準輸出。

此代碼編譯時不會出錯,但是一旦運行就會拋出運行時錯誤,因為這里試圖將字符類型映射為整形。很顯然,如果隱藏了必要信息,類型系統將不能幫助寫出安全代碼。

為了解決這個問題,需要為存入集合中的對象指定具體類型:

List<Integer> listOfNumbers = new ArrayList<>();listOfNumbers.add(10); listOfNumbers.add("Twenty");listOfNumbers.forEach(n -> System.out.println((int) n * 2)); 

與之前代碼的唯一差別即是定義集合的那一行:

復制代碼 代碼如下:

 List<Integer> listOfNumbers = new ArrayList<>();

修改之后的代碼編譯不可能被通過,因為這里試圖向只期望存儲整形的集合中添加字符串。編譯器將會顯示錯誤信息,并指向試圖向列表中添加 Twenty 字符的那一行。參數化泛型類型是個不錯的主意。這樣的話,編譯器就能夠檢查所有可能的類型,從而由于類型不一致而導致的運行時異常幾率將大大降低。

主要總結了以上五個Java程序員常犯的錯誤,希望大家能夠喜歡。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
中文字幕久热精品在线视频| 久久久噜噜噜久久久| 久久夜色精品国产| 日韩视频在线免费观看| 成人黄色免费网站在线观看| 91精品国产高清久久久久久| 亚洲欧洲日韩国产| 亚洲香蕉av在线一区二区三区| 亚洲桃花岛网站| 这里只有精品视频在线| 日韩电视剧免费观看网站| 日韩精品视频在线播放| 91美女片黄在线观看游戏| 国产精品女主播| 久久久久99精品久久久久| 国产精品久久不能| 久久精品人人做人人爽| 精品国产自在精品国产浪潮| 色老头一区二区三区在线观看| 欧美日本黄视频| 欧美成人一二三| 欧美性xxxx| 亚洲精品99999| 久久精品一偷一偷国产| 久久国产精品久久国产精品| 国产精品偷伦视频免费观看国产| 欧美成人精品在线观看| 国产69精品久久久| 奇米成人av国产一区二区三区| 孩xxxx性bbbb欧美| 欧美中文在线视频| 日韩国产一区三区| 不用播放器成人网| 欧美裸体xxxx极品少妇| 97成人精品区在线播放| xvideos国产精品| 欧美视频一区二区三区…| 欧美国产日韩一区二区| 91精品视频在线免费观看| 亚洲人成网在线播放| 成人亚洲激情网| 亚洲免费精彩视频| 久久深夜福利免费观看| 欧美大片大片在线播放| 日韩美女av在线免费观看| 亚洲人午夜精品| 亚洲国产精品一区二区久| 久久影视电视剧凤归四时歌| 九色91av视频| 亚洲成人黄色在线观看| 国产精品高潮粉嫩av| 亚洲精品中文字幕有码专区| 久久久久久999| 亚洲第一精品福利| 欧美在线观看网站| 日韩精品日韩在线观看| 欧美插天视频在线播放| 亚洲成年人在线| 日韩精品在线免费观看视频| 国产成人精品久久二区二区| 懂色av中文一区二区三区天美| 高清欧美电影在线| 欧美激情va永久在线播放| 992tv成人免费视频| 日韩欧美国产高清91| 精品国产乱码久久久久久天美| 亚洲a级在线播放观看| 亚洲品质视频自拍网| 欧美黑人一级爽快片淫片高清| 久久久久久91香蕉国产| 色综合久久88色综合天天看泰| 麻豆国产精品va在线观看不卡| 亚洲精品欧美日韩专区| 美女福利视频一区| 91免费精品国偷自产在线| 日本人成精品视频在线| 久久人人爽人人爽人人片av高清| 国模精品视频一区二区三区| 日韩国产中文字幕| 日韩欧美在线观看视频| 亚洲v日韩v综合v精品v| 国产精品久久久久久久久久| 91成人天堂久久成人| 亚洲欧美一区二区激情| 色av吧综合网| 不卡av日日日| 欧美日韩性生活视频| 国产97在线观看| 精品国产91久久久| 亚洲精品自产拍| 97av在线影院| 欧美老少做受xxxx高潮| 久久精品人人爽| 欧美激情精品久久久久| 久久亚洲影音av资源网| 亚洲福利视频久久| 久久影院资源网| 亚洲变态欧美另类捆绑| 日韩精品免费综合视频在线播放| 成人写真视频福利网| 国产成人在线播放| 亚洲精品一区久久久久久| 福利视频第一区| 欧美一级片在线播放| 久久精品视频在线观看| 国模gogo一区二区大胆私拍| 欧美在线观看视频| 精品高清美女精品国产区| 日本欧美中文字幕| 在线播放国产一区中文字幕剧情欧美| 欧美成人性生活| 亚洲国产中文字幕在线观看| 亚洲精品一区二三区不卡| 国产日韩在线精品av| 久久夜精品香蕉| 亚洲人成77777在线观看网| 亚洲淫片在线视频| 成人国产精品免费视频| 精品爽片免费看久久| 26uuu国产精品视频| www.久久撸.com| 亚洲人成网站色ww在线| 久久久人成影片一区二区三区| 色婷婷**av毛片一区| 久久久人成影片一区二区三区观看| 亚洲精品国产精品乱码不99按摩| 色偷偷91综合久久噜噜| 97国产真实伦对白精彩视频8| 国产成人精品久久二区二区91| 欧美国产日韩免费| 久久99精品久久久久久琪琪| 91精品国产99久久久久久| 国产亚洲精品久久久久动| 久久久av网站| 国产精品久久久久久亚洲调教| 96sao精品视频在线观看| 久久精品中文字幕电影| 亚洲另类激情图| 久久999免费视频| 91美女高潮出水| 国产精品久久久久久久久久久久| 国产97人人超碰caoprom| 亚洲第一福利在线观看| 欧美一级电影在线| 亚洲欧洲日韩国产| 综合国产在线观看| 久久精品国产视频| 粗暴蹂躏中文一区二区三区| 成人春色激情网| 亚洲最大的av网站| 97精品视频在线| 亚洲国产成人精品一区二区| 欧美激情区在线播放| 欧美精品九九久久| 亚洲精品日韩丝袜精品| 91热精品视频| 国产精品第1页| 久久久久久这里只有精品| 黄色一区二区在线观看| 欧美日韩精品在线| 俺去啦;欧美日韩| 日韩中文在线不卡| 黄网站色欧美视频| 亚洲精品自拍偷拍|