阿裡巴巴編碼規範(Java)證明

来源:https://www.cnblogs.com/xiexj/archive/2020/05/28/12970031.html
-Advertisement-
Play Games

背景 阿裡雲上有個阿裡巴巴編碼規範認證,我估算一下時間成本很低,多個認證也沒什麼壞處,就花了1分錢報了個名。這個認證報名後就可以下載鏈接下的編碼規範,然後參加個考試應該就OK了。 共48頁的規範實際上每讀一遍都是要花一些時間的,因為每讀一遍就會發現上面有些東西我不信。我需要去證明。過去證明過的因為J ...


背景

阿裡雲上有個阿裡巴巴編碼規範認證,我估算一下時間成本很低,多個認證也沒什麼壞處,就花了1分錢報了個名。這個認證報名後就可以下載鏈接下的編碼規範,然後參加個考試應該就OK了。 

共48頁的規範實際上每讀一遍都是要花一些時間的,因為每讀一遍就會發現上面有些東西我不信。我需要去證明。過去證明過的因為JDK版本升級迭代有可能需要繼續證明。下麵是其中的一些證明過程。

 

案例1

規範原文

【強制】不要在foreach迴圈里進行元素的remove/add操作。remove元素請使用Iterator方式,如果併發操作需要對Iterator對象加鎖。

正例:

List<String> list = new ArrayList<>();

list.add("1");

list.add("2");

Iterator<String> iteraot = list.interator();

while(iterator.hasNext()){

    String item = iterator.next();

    if(刪除元素的條件) {

          iterator.remove();

    }

}

反例:

for(String item : list) {

      if("1".equals(item)) {

             list.remove(item);

      }

}

說明:以上代碼的執行結果肯定會出乎大家的意料,那麼試一下把"1"換成"2",會是同樣的結果嗎?

證明

1.先按照反例例文運行測試(test1)

list里兩個元素,remove掉一個,剩下1個。這應該是符合大多數人預期的。

2.按照說明把"1"換成"2"運行測試(test2)

這裡沒有按照預期remove掉"2",而是拋出了併發修改異常。點擊到異常的地方

3.根據異常提示。找到拋出異常代碼的地方查看是哪個方法拋出異常:

4.對源碼做一個解析:

拋出併發修改異常的條件是modCount!=expectedModCount。

5.根據這個條件,我做一個推測:在一個操作里把這兩者的值改的不一樣了,因為這裡調用了remove修改方法。我自然就推測是remove方法做的修改,來看remove方法的源碼:

6.果然,源碼中將modCount++,但是expectedModCount並沒有修改。證明瞭推測。運行完remove後需要判斷for(String item : list) ,這時候調用了迭代器的next方法。這樣我理解了上面test2里為什麼會拋出異常。那麼再來思考下test1為什麼不拋出異常呢?

7.我們來debug一下test1的情況1

運行完remove方法後,可看到這時候modCount!=expectedModCount,但是這時候只執行了hasNext(),判斷了cursor != size,這時候不會執行next方法,所以不會產生異常。而下麵再用到list時迭代器是新的迭代器,會把modCount=expectedModCount;

結論

如果list在for迴圈里調用remove方法是會拋出併發修改異常的,但是如果只修改了第1個就返回的情況是個例外,因為這時候不會調用next方法判斷modCount和expectedModCount是否相等。

使用代碼規範推薦的迭代器,底層remove方法會將modCount和expectedModCount一起修改,所以單線程不會有併發問題,作為類的成員變數,多線程情況下被修改就不確定了。

思考題

下麵代碼的執行結果是多少?

案例2

規範原文

【強制】在JDK7版本及以上,Comparotor實現類要滿足如下三個條件,不然Arrays.sort、Collections.sort會拋IllegalArgumentException異常。

說明:三個條件如下

1)x、y的比較結果和y、x的比較結果相反。

2)x>y, y>z,則x>z。

3)x=y,則x,z比較結果和y,z比較結果相同。

反例:下例中沒有處理相等的情況,交換兩個對象判斷結果並不互反,不符合第一個條件,在實際使用中可能會出現異常。

new Comparotor<Student>() {

    @Override

    public int compare(Student o1, Student o2) {

           return o1.getId()>o2.getId()?1:-1;

    }

}

證明

1.我們先來看看反例在實際使用中會拋出什麼異常。

 

 

2.測試發現不論是Collections.sort還是Arrays.sort都拋出錯誤說必須實現Comparable介面而不是Comparator介面。而Comparable介面是不需要滿足規範里所說的自反性、傳遞性和對稱性的。

那為什麼規範里會這麼說呢?

3.我查了Collections類的源碼,確實有幾個方法用到了Comparator類。包括反轉、二分查找、最大值、最小值和幾個sort等。後面的原來sort是可以後面接Comparator參數的。

4. 然而我用源碼的兩個參數形式傳入後,運行了幾個例子並沒有拋出非法參數異常。於是我又在源碼中找線索。

Collections.sort底層用了Arrays.sort,Arrays.sort底層用了TimSort,TimSort有兩處會拋出這個異常,看源碼是在二分查找merge結果的時候。

5.使用這個sort果然是發生了非法參數異常。

6.具體為什麼會發生異常,我打了斷點,使用debug跟了一下TimSort源碼,大體是有部分排序使用了if(x<y) else 判斷,又一些方法里使用了if(x<=y)來判斷。這兩個結果對於等於的情況是衝突的。這時候會發生異常。

總結

實現Comparator的compare方法要滿足自反性、對稱性、傳遞性。

案例3

規範原文

分析

1.從上面總結來看線程安全的Map的key和value都不能為null。線程不安全的可以為null。大家都知道map的key要進行hash。對null進行hash不會空指針嗎?

帶著這個疑問,打開HashMap的源碼看到hash方法有對null做判斷,如果null則hash值為0。所以不會NPE

 

2.TreeMap的put方法沒有對key做任何的判斷,然後會調用compare方法,這裡會拋出NPE

3.那麼對於key如果不做特殊處理,肯定是要拋出NPE的,應該沒有什麼疑問了。為什麼有的value為空也會NPE呢?

從上面源碼可知道ConcurrentHashMap就是這麼處理的,算是強制。

 

4.而Hashtable也是強制。

 

5.為什麼線程安全的容器要設計成key和value不能為null呢?在網上找到了類設計者Lea的原話,主要表達的意思是因為map需要實現containsKey和containsValue方法。這個方法對於null的情況實際上是用get(XX)來實現的,如果為null就不好區分到底是因為不存在還是值就是null。

6.而線程不安全的就是按單線程處理,下麵是TreeMap里containsValue的處理,如果為輸入為null,並且有個對象值為null就是true了。

總結

四種常用map中線程安全的Map的key和value都不能為null。線程不安全的value都可以為null。TreeMap的key不能為null。

案例4

規範原文 

【強制】多線程並行處理定時任務時,Timer 運行多個 TimeTask 時,只要其中之一沒有捕獲 拋出的異常,其它任務便會自動終止運行,如果在處理定時任務時使用ScheduledExecutorService 則沒有這個問題。 

證明

1.先讓Timer 運行多個 TimeTask,讓其中之一沒有捕獲 拋出的異常

這段代碼的意思是在10秒內運行兩個定時任務,其中一個定時任何每10ms做前後列印。另外一個拋出異常,結果拋出異常後兩個都停止了。

2.從Timer源代碼可知,本質上多個任務通過一個隊列來維護。處理的時候整個過程整體try catch。那麼一個出異常整個過程都停止了。

3.再驗證使用ScheduledExecutorService的情況, 可看到拋出異常的線程運行了一次之後就停止了,另外一個線程一直繼續運行。

4. 從源碼可知如果一個工作線程出現了問題會直接從工作隊列里移除,不影響其他的。

 

總結

ScheduledExecutorService相比Timer能避免多個任務之間的出現問題時的副作用。

案例5

規範原文

【強制】使用工具類Arrays.asList()把數組轉換成集合時,不能使用其修改集合相關的方 法,它的 add/remove/clear 方法會拋出 UnsupportedOperationException 異常。說明:asList 的返回對象是一個 Arrays 內部類,並沒有實現集合的修改方法。Arrays.asList 體現的是適 配器模式,只是轉換介面,後臺的數據仍是數組。

String[] str = new String[] { "yang", "hao" };

List list = Arrays.asList(str);第一種情況:list.add("yangguanbao"); 運行時異常。第二種情況:str[0] = "changed"; 也會隨之修改,反之亦然。

證明

1.Arrays.asList()把數組轉換成集合後添加元素,測試運行,果然拋出異常

跟蹤源碼可知道雖然asList生成的是ArrayList,但它並不是java.util.ArrayList,而是Arrays里自定義的。這個類不支持這些更新操作。

總結

小心集合類中返回一個子集或者轉換類型的操作,可能返回的是內部定義的類,不是我們平時用的類,這些類中對一些操作做了限制。

思考題

下麵測試類體現了規範的哪一條?

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在我開發的很多系統裡面,包括Winform混合框架、Bootstrap開發框架等系列產品中,我都傾向於動態配置菜單,並管理對應角色的菜單許可權和頁面許可權,實現系統對用戶許可權的控制,菜單一般包括有名稱、圖標、順序、URL連接等相關信息,對於VUE+Element 前端應用來說,應該原理上差不多,本篇隨筆... ...
  • 關於瀏覽器相容問題 目前主流瀏覽器有IE,edge,Chrome,firefox,Opera(歐朋),Safari(指南針) 由於目前的電腦操作系統很多,瀏覽器的版本迭代有很強,因此用戶的使用瀏覽器是參差不齊的,因此我們作為前端人員在設計網站在不同瀏覽器顯示效果時,相容性就很有必要考慮,最追求給用戶 ...
  • 第一步: 首先進入自己的博客園 -- 點擊<<設置>>--找到 <<博客側邊欄公告(支持HTML代碼) (支持 JS 代碼)>> 第二步: 加入如下代碼 <img style="width: 200px; height: 200px;" src="頭像路徑" alt="qq377905687的頭像" ...
  • 方法 說明 歸屬 alert(msg) 瀏覽器彈出警示框 瀏覽器 console.log(msg) 瀏覽器控制台列印輸出信息 瀏覽器 prompt(info) 瀏覽器彈出輸入框,用戶可以輸入 瀏覽器 程式示例: <!DOCTYPE html> <html lang="en"> <head> <met ...
  • 1.行內式js(很少使用) 以on開頭,如onclick HTML中推薦雙引號,JS推薦單引號 2.內嵌式js(常用) <script> alert('hello world'); <script> 3.外部js文件 <script src="my.js"></script> 利於代碼結構化 便於文 ...
  • 突發奇想,感覺ui組件的上傳圖片用著很方便,突然件想自己寫一個探索其原理 頭像上傳功能主要是前端通過formData將文件傳至伺服器然後伺服器將圖片保存至文件夾,並將其路徑返回至前端,前端渲染至頁面。 前端頁面如下所示(主要是為了寫一個簡單的例子): js部分:這裡使用的是原生的ajax請求將文件信 ...
  • 代碼示例:新聞頁面: 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1 ...
  • 對於那些存在對象之間複雜交互關係的系統,中介者模式提供了一種簡化複雜交互的解決方案,即通過引入一個中介者,將原本對象之間的兩兩交互轉化為每個對象與中介者之間的交互 模式動機 以微信聊天為例,可以用戶與用戶直接聊天,也可以通過微信群聊天。前者的話,用戶要和別的用戶加為好友,即用戶和用戶之間存在多對多關 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...