背景
阿里云上有個阿里巴巴編碼規范認證,我估算一下時間成本很低,多個認證也沒什么壞處,就花了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里自定義的,這個類不支持這些更新操作,

總結
小心集合類中回傳一個子集或者轉換型別的操作,可能回傳的是內部定義的類,不是我們平時用的類,這些類中對一些操作做了限制,
思考題
下面測驗類體現了規范的哪一條?
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/179237.html
標籤:Java
上一篇:微服務專案持續集成部署流程簡介
