下載原版阿里JAVA開發手冊 【阿里巴巴Java開發手冊v1.2.0】html
本文主要是對照阿里開發手冊,註釋本身在工做中運用狀況。java
本文內容:OOP 規約 、集合處理 、併發處理 、其餘正則表達式
本文難度係數爲三星(★★★) 本文爲第二篇數據庫
第一篇 點評阿里JAVA手冊之編程規約(命名風格、常量定義、代碼風格、控制語句、註釋規約)編程
第二篇 點評阿里JAVA手冊之編程規約(OOP 規約 、集合處理 、併發處理 、其餘)設計模式
第三篇 點評阿里JAVA手冊之異常日誌(異常處理 日誌規約 )api
第四篇 點評阿里JAVA手冊之MySQL數據庫 (建表規約、索引規約、SQL語句、ORM映射)數組
代碼的字裏行間流淌的是軟件生命中的血液,質量的提高是儘量少踩坑,杜絕踩重複的坑,切實提高質量意識。另外,現代軟件架構都須要協同開發完成,高效協做即下降協同成本,提高溝通效率,所謂無規矩不成方圓,無規範不能協做。衆所周知,制訂交通法規表面上是要限制行車權,其實是保障公衆的人身安全。試想若是沒有限速,沒有紅綠燈,誰還敢上路行駛。對軟件來講,適當的規範和標準毫不是消滅代碼內容的創造性、優雅性,而是限制過分個性化,以一種廣泛承認的統一方式一塊兒作事,提高協做效率。緩存
(六) OOP 規約安全
1. 【強制】避免經過一個類的對象引用訪問此類的靜態變量或靜態方法,無謂增長編譯器解析成本,直接用類名來訪問便可。
【點評】規則好,已經遵循。
public class Student { public static final int MIN_AGE = 6; } Student st = new Student(); //錯誤 int minAge=st.MIN_AGE; //正確 int minAge=Student.MIN_AGE;
2. 【強制】全部的覆寫方法,必須加@Override 註解。
說明:getObject()與 get0bject()的問題。一個是字母的 O,一個是數字的 0,加@Override 能夠準確判斷是否覆蓋成功。
另外,若是在抽象類中對方法簽名進行修改,其實現類會立刻編譯報錯。
【點評】規則好,已經遵循。
3. 【強制】相同參數類型,相同業務含義,纔可使用 Java 的可變參數,避免使用 Object。 說明:可變參數必須放置在參數列表的最後。(提倡同窗們儘可能不用可變參數編程) 正例:public User getUsers(String type, Integer... ids) {...}
【點評】規則好,已經遵循。可變參數使用例子: String.format
4. 【強制】外部正在調用或者二方庫依賴的接口,不容許修改方法簽名,避免對接口調用方產生影響。接口過期必須加@Deprecated 註解,並清晰地說明採用的新接口或者新服務是什麼。
【點評】規則好,已經遵循。
5. 【強制】不能使用過期的類或方法。 說明:java.net.URLDecoder 中的方法 decode(String encodeStr) 這個方法已通過時,應 該使用雙參數 decode(String source, String encode)。接口提供方既然明確是過期接口, 那麼有義務同時提供新的接口;做爲調用方來講,有義務去考證過期方法的新實現是什麼。
【點評】規則好,已經遵循。如代碼中有未遵循的狀況
如 java.net.URLEncoder url=new java.net.URLEncoder(); url.encode(s)
6. 【強制】Object 的 equals 方法容易拋空指針異常,應使用常量或肯定有值的對象來調用 equals。
正例: "test".equals(object);
反例: object.equals("test");
說明:推薦使用 java.util.Objects#equals (JDK7 引入的工具類)
【點評】規則好,已經遵循。未遵循推薦的工具類。
7. 【強制】全部的相同類型的包裝類對象之間值的比較,所有使用 equals 方法比較。
說明:對於 Integer var = ? 在-128 至 127 範圍內的賦值,Integer 對象是在 IntegerCache.cache 產生,會複用已有對象,這個區間內的 Integer 值能夠直接使用==進行 判斷,可是這個區間以外的全部數據,都會在堆上產生,並不會複用已有對象,這是一個大坑, 推薦使用 equals 方法進行判斷。
【點評】規則好,已經遵循。 對於對象比較,都使用equals 方法比較。
8. 關於基本數據類型與包裝數據類型的使用標準以下:
1) 【強制】全部的 POJO 類屬性必須使用包裝數據類型。
2) 【強制】RPC 方法的返回值和參數必須使用包裝數據類型。
3) 【推薦】全部的局部變量使用基本數據類型。
說明:POJO 類屬性沒有初值是提醒使用者在須要使用時,必須本身顯式地進行賦值,任何 NPE 問題,或者入庫檢查,都由使用者來保證。
正例:數據庫的查詢結果多是 null,由於自動拆箱,用基本數據類型接收有 NPE 風險。
反例:好比顯示成交總額漲跌狀況,即正負 x%,x 爲基本數據類型,調用的 RPC 服務,調用不成功時,返回的是默認值,頁面顯示:0%,這是不合理的,應該顯示成中劃線-。因此包裝數據類型的 null 值,可以表示額外的信息,如:遠程調用失敗,異常退出。
【點評】規則好,已經遵循。 方法內的變量(局部變量)使用基本類型。調用者進行java.lang.NullPointerException檢查。
9. 【強制】定義 DO/DTO/VO 等 POJO 類時,不要設定任何屬性默認值。
反例:POJO 類的 gmtCreate 默認值爲 new Date();可是這個屬性在數據提取時並無置入具體值,在更新其它字段時又附帶更新了此字段,致使建立時間被修改爲當前時間。
【點評】規則好,已經遵循。
10. 【強制】序列化類新增屬性時,請不要修改serialVersionUID 字段,避免反序列失敗;若是徹底不兼容升級,避免反序列化混亂,那麼請修改serialVersionUID 值。
說明:注意 serialVersionUID 不一致會拋出序列化運行時異常。
【點評】規則好,已經遵循。
11. 【強制】構造方法裏面禁止加入任何業務邏輯,若是有初始化邏輯,請放在 init 方法中。
【點評】規則好,已經遵循。
12. 【強制】POJO 類必須寫 toString 方法。使用 IDE 的中工具:source> generate toString 時,若是繼承了另外一個 POJO 類,注意在前面加一下 super.toString。 說明:在方法執行拋出異常時,能夠直接調用 POJO 的 toString()方法打印其屬性值,便於排查問題。
【點評】規則存疑,未遵循。
主要是根據需求來,當默認的toString()不能知足你對」文本方式表示此對象「時,重寫toString(),例如bean類須要在重寫的toString 方法中組織本身想要顯示的當前對象的信息。
官方文檔以下:
public String toString()
返回該對象的字符串表示。一般,toString 方法會返回一個「以文本方式表示」此對象的字符串。結果應是一個簡明但易於讀懂的信息表達式。建議全部子類都重寫此方法。
Object 類的 toString 方法返回一個字符串,該字符串由類名(對象是該類的一個實例)、at 標記符「@」和此對象哈希碼的無符號十六進制表示組成。換句話說,該方法返回一個字符串,它的值等於:
getClass().getName() + '@' + Integer.toHexString(hashCode())
返回:
該對象的字符串表示形式。
13. 【推薦】使用索引訪問用 String 的 split 方法獲得的數組時,需作最後一個分隔符後有無內容的檢查,不然會有拋 IndexOutOfBoundsException 的風險。 說明: String str = "a,b,c,,"; String[] ary = str.split(","); //預期大於 3,結果是 3 System.out.println(ary.length);
【點評】規則好,已經遵循。
14. 【推薦】當一個類有多個構造方法,或者多個同名方法,這些方法應該按順序放置在一塊兒, 便於閱讀。
【點評】規則好,已經遵循。
15. 【推薦】 類內方法定義順序依次是:公有方法或保護方法 > 私有方法 > getter/setter 方法。 說明:公有方法是類的調用者和維護者最關心的方法,首屏展現最好;保護方法雖然只是子類 關心,也多是「模板設計模式」下的核心方法;而私有方法外部通常不須要特別關心,是一個黑盒實現;由於方法信息價值較低,全部 Service 和 DAO 的 getter/setter 方法放在類體最後。
【點評】規則好,不過未嚴格遵循。
16. 【推薦】setter 方法中,參數名稱與類成員變量名稱一致,this.成員名 = 參數名。在 getter/setter 方法中,不要增長業務邏輯,增長排查問題的難度。
反例: public Integer getData() {
if (true) { return this.data + 100; }
else {
return this.data - 100; }
}
【點評】規則好,不過未嚴格遵循。
17. 【推薦】循環體內,字符串的鏈接方式,使用 StringBuilder 的 append 方法進行擴展。 說明:反編譯出的字節碼文件顯示每次循環都會 new 出一個 StringBuilder 對象,而後進行 append 操做,最後經過 toString 方法返回 String 對象,形成內存資源浪費。
反例: String str = "start";
for (int i = 0; i < 100; i++) {
str = str + "hello";
}
【點評】規則好,嚴格遵循。
18. 【推薦】final 能夠聲明類、成員變量、方法、以及本地變量,下列狀況使用 final 關鍵字:
1) 不容許被繼承的類,如:String 類。
2) 不容許修改引用的域對象,如:POJO 類的域變量。
3) 不容許被重寫的方法,如:POJO 類的 setter 方法。
4) 不容許運行過程當中從新賦值的局部變量。
5) 避免上下文重複使用一個變量,使用 final 描述能夠強制從新定義一個變量,方便更好地進行重構。
【點評】規則好,嚴格遵循。
19. 【推薦】慎用 Object 的 clone 方法來拷貝對象。
說明:對象的 clone 方法默認是淺拷貝,若想實現深拷貝須要重寫 clone 方法實現屬性對象的拷貝。
【點評】規則好,嚴格遵循。BeanUtils.copyProperties(apiProcessor, this);來clone
20. 【推薦】類成員與方法訪問控制從嚴:
1) 若是不容許外部直接經過 new 來建立對象,那麼構造方法必須是 private。
2) 工具類不容許有 public 或 default 構造方法。
3) 類非 static 成員變量而且與子類共享,必須是 protected。
4) 類非 static 成員變量而且僅在本類使用,必須是 private。
5) 類 static 成員變量若是僅在本類使用,必須是 private。
6) 如果 static 成員變量,必須考慮是否爲 final。
7) 類成員方法只供類內部調用,必須是 private。
8) 類成員方法只對繼承類公開,那麼限制爲 protected。 說明:任何類、方法、參數、變量,嚴控訪問範圍。過於寬泛的訪問範圍,不利於模塊解耦。 思考:若是是一個 private 的方法,想刪除就刪除,但是一個 public 的 service 方法,或者 一個 public 的成員變量,刪除一下,不得手心冒點汗嗎?變量像本身的小孩,儘可能在本身的 視線內,變量做用域太大,若是無限制的處處跑,那麼你會擔憂的。
【點評】規則好,嚴格遵循。
(七) 集合處理
1. 【強制】關於 hashCode 和 equals 的處理,遵循以下規則:
1) 只要重寫equals,就必須重寫hashCode。
2) 由於 Set 存儲的是不重複的對象,依據hashCode 和 equals 進行判斷,因此 Set 存儲的對象必須重寫這兩個方法。
3) 若是自定義對象作爲 Map 的鍵,那麼必須重寫 hashCode 和 equals。
說明:String 重寫hashCode 和 equals 方法,因此咱們能夠很是愉快地使用 String 對象做爲 key 來使用。
【點評】規則好,嚴格遵循。
2. 【強制】 ArrayList的subList結果不可強轉成ArrayList,不然會拋出ClassCastException 異常:java.util.RandomAccessSubList cannot be cast to java.util.ArrayList ;
說明:subList 返回的是 ArrayList 的內部類 SubList,並非 ArrayList ,而是 ArrayList 的一個視圖,對於 SubList 子列表的全部操做最終會反映到原列表上。
【點評】規則好,嚴格遵循。
3. 【強制】 在 subList 場景中,高度注意對原集合元素個數的修改,會致使子列表的遍歷、增長、刪除均產生 ConcurrentModificationException 異常。
【點評】規則好,嚴格遵循。 多線程對同一HashMap的修改,也會出現ConcurrentModificationException
4. 【強制】使用集合轉數組的方法,必須使用集合的 toArray(T[] array),傳入的是類型徹底 同樣的數組,大小就是 list.size()。
說明:使用 toArray 帶參方法,入參分配的數組空間不夠大時,toArray 方法內部將從新分配 內存空間,並返回新數組地址;若是數組元素大於實際所需,下標爲[ list.size() ]的數組 元素將被置爲 null,其它數組元素保持原值,所以最好將方法入參數組大小定義與集合元素 個數一致。 正例:
List<String> list = new ArrayList<String>(2);
list.add("guan");
list.add("bao");
String[] array = new String[list.size()];
array = list.toArray(array);
反例:直接使用 toArray 無參方法存在問題,此方法返回值只能是 Object[]類,若強轉其它 類型數組將出現 ClassCastException 錯誤。
【點評】規則好,嚴格遵循,代碼中不多用toArray 。
5. 【強制】使用工具類 Arrays.asList()把數組轉換成集合時,不能使用其修改集合相關的方 法,它的 add/remove/clear 方法會拋出 UnsupportedOperationException 異常。
說明:asList 的返回對象是一個 Arrays 內部類,並無實現集合的修改方法。Arrays.asList 體現的是適配器模式,只是轉換接口,後臺的數據還是數組。
String[] str = new String[] { "a", "b" }; List list = Arrays.asList(str);
第一種狀況:list.add("c"); 運行時異常。 第二種狀況:str[0] = "gujin"; 那麼 list.get(0)也會隨之修改。
【點評】規則好,嚴格遵循
6. 【強制】泛型通配符<? extends T>來接收返回的數據,此寫法的泛型集合不能使用 add 方 法,而<? super T>不能使用 get 方法,作爲接口調用賦值時易出錯。
說明:擴展說一下 PECS(Producer Extends Consumer Super)原則:
1)頻繁往外讀取內容 的,適合用上界 Extends。
2)常常往裏插入的,適合用下界 Super。
【點評】規則好,嚴格遵循
Java 泛型
關鍵字說明
? 通配符類型
<? extends T> 表示類型的上界,表示參數化類型的多是T 或是 T的子類
<? super T> 表示類型下界(Java Core中叫超類型限定),表示參數化類型是此類型的超類型(父類型),直至Object
extends 示例
static class Food{}
static class Fruit extends Food{}
static class Apple extends Fruit{}
static class RedApple extends Apple{}
List<? extends Fruit> flist = new ArrayList<Apple>();
// complie error:
// flist.add(new Apple());
// flist.add(new Fruit());
// flist.add(new Object());
flist.add(null); // only work for null
List<? extends Frut> 表示 「具備任何從Fruit繼承類型的列表」,編譯器沒法肯定List所持有的類型,因此沒法安全的向其中添加對象。能夠添加null,由於null 能夠表示任何類型。因此List 的add 方法不能添加任何有意義的元素,可是能夠接受現有的子類型List<Apple> 賦值。
Fruit fruit = flist.get(0);
Apple apple = (Apple)flist.get(0);
因爲,其中放置是從Fruit中繼承的類型,因此能夠安全地取出Fruit類型。
flist.contains(new Fruit());
flist.contains(new Apple());
在使用Collection中的contains 方法時,接受Object 參數類型,能夠不涉及任何通配符,編譯器也容許這麼調用。
super 示例
List<? super Fruit> flist = new ArrayList<Fruit>();
flist.add(new Fruit());
flist.add(new Apple());
flist.add(new RedApple());
// compile error:
List<? super Fruit> flist = new ArrayList<Apple>();
List<? super Fruit> 表示「具備任何Fruit超類型的列表」,列表的類型至少是一個 Fruit 類型,所以能夠安全的向其中添加Fruit 及其子類型。因爲List<? super Fruit>中的類型多是任何Fruit 的超類型,沒法賦值爲Fruit的子類型Apple的List<Apple>.
// compile error:
Fruit item = flist.get(0);
由於,List<? super Fruit>中的類型多是任何Fruit 的超類型,因此編譯器沒法肯定get返回的對象類型是Fruit,仍是Fruit的父類Food 或 Object.
小結
extends 可用於的返回類型限定,不能用於參數類型限定。
super 可用於參數類型限定,不能用於返回類型限定。
>帶有super超類型限定的通配符能夠向泛型對易用寫入,帶有extends子類型限定的通配符能夠向泛型對象讀取。
7. 【強制】不要在 foreach 循環裏進行元素的 remove/add 操做。
remove 元素請使用 Iterator 方式,若是併發操做,須要對 Iterator 對象加鎖。
正例: Iterator<String> it = a.iterator();
while (it.hasNext()) {
String temp = it.next();
if (刪除元素的條件) {
it.remove();
}
}
反例: List<String> a = new ArrayList<String>();
a.add("1");
a.add("2");
for (String temp : a) {
if ("1".equals(temp)) {
a.remove(temp);
}
}
說明:以上代碼的執行結果確定會出乎你們的意料,那麼試一下把「1」換成「2」,會是一樣的結果嗎? ConcurrentModificationException
【點評】規則好,嚴格遵循
8. 【強制】 在 JDK7 版本及以上,Comparator 要知足以下三個條件,否則 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 Comparator<Student>() {
@Override public int compare(Student o1, Student o2)
{ return o1.getId() > o2.getId() ? 1 : -1;
}
};
【點評】規則好,嚴格遵循
9. 【推薦】集合初始化時,指定集合初始值大小。
說明:HashMap 使用 HashMap(int initialCapacity) 初始化,
正例:initialCapacity = (須要存儲的元素個數 / 負載因子) + 1。
注意負載因子(即loader factor)默認爲 0.75,若是暫時沒法肯定初始值大小,
請設置爲 16。
反例:HashMap 須要放置 1024 個元素,因爲沒有設置容量初始大小,隨着元素不斷增長,容量 7 次被迫擴大,resize 須要重建 hash 表,嚴重影響性能。
【點評】規則好,未嚴格遵循。在影響性能的狀況下須要嚴格遵循
10. 【推薦】使用 entrySet 遍歷 Map 類集合 KV,而不是 keySet 方式進行遍歷。
說明:keySet 實際上是遍歷了 2 次,一次是轉爲 Iterator 對象,另外一次是從 hashMap 中取出 key 所對應的 value。而 entrySet 只是遍歷了一次就把 key 和 value 都放到了 entry 中,效 率更高。
若是是 JDK8,使用 Map.foreach 方法。
正例:values()返回的是 V 值集合,是一個 list 集合對象;
keySet()返回的是 K 值集合,是 一個 Set 集合對象;entrySet()返回的是 K-V 值組合集合。
【點評】規則好,嚴格遵循
第一種:
Map map = new HashMap();
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
Object key = entry.getKey();
Object val = entry.getValue();
}
效率高,之後必定要使用此種方式!
第二種:
Map map = new HashMap();
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
Object key = iter.next();
Object val = map.get(key);
}
效率低,之後儘可能少使用!
11. 【推薦】高度注意 Map 類集合 K/V 能不能存儲 null 值的狀況,以下表格:
集合類 |
Key |
Value |
Super Class |
說明 |
Hashtable |
不容許爲null |
不容許爲null |
Dictionary |
線程安全 |
ConcurrentHashMap |
不容許爲 null |
不容許爲 null |
AbstractMap |
分段鎖技術 |
TreeMap |
不容許爲 null |
容許爲 null |
AbstractMap |
線程不安全 |
HashMap |
容許爲 null |
容許爲 null |
AbstractMap |
線程不安全 |
反例: 因爲 HashMap 的干擾,不少人認爲 ConcurrentHashMap 是能夠置入 null 值,而事實上, 存儲 null 值時會拋出 NPE 異常。
【點評】規則好,嚴格遵循
12. 【參考】合理利用好集合的有序性(sort)和穩定性(order),避免集合的無序性(unsort)和不穩定性(unorder)帶來的負面影響。 說明:有序性是指遍歷的結果是按某種比較規則依次排列的。穩定性指集合每次遍歷的元素次序是必定的。如:ArrayList 是 order/unsort;HashMap 是 unorder/unsort;TreeSet 是 order/sort。
【點評】規則好,嚴格遵循
13. 【參考】利用 Set 元素惟一的特性,能夠快速對一個集合進行去重操做,避免使用 List 的 contains 方法進行遍歷、對比、去重操做。
【點評】規則好,嚴格遵循
(八) 併發處理
1. 【強制】獲取單例對象須要保證線程安全,其中的方法也要保證線程安全。 說明:資源驅動類、工具類、單例工廠類都須要注
【點評】規則好,嚴格遵循意。
2. 【強制】建立線程或線程池時請指定有意義的線程名稱,方便出錯時回溯。
正例: public class TimerTaskThread extends Thread {
public TimerTaskThread() { super.setName("TimerTaskThread"); ... }
【點評】規則存疑,匿名類呢?
3. 【強制】線程資源必須經過線程池提供,不容許在應用中自行顯式建立線程。 說明:使用線程池的好處是減小在建立和銷燬線程上所花的時間以及系統資源的開銷,解決資
源不足的問題。若是不使用線程池,有可能形成系統建立大量同類線程而致使消耗完內存或者 「過分切換」的問題。
【點評】規則好,未嚴格遵循。須要根據實際應用場景而定。
線程池模型 ,線程須要常常重複建立用線程池。適用於單次任務執行時間較短,但併發訪問量高的狀況。當處理線程數設置極大的時候和非線程池模型幾乎沒有差異
非線程池模型:適用於單次鏈接任務執行時間較長,併發量不高的狀況。
4. 【強制】線程池不容許使用 Executors 去建立,而是經過 ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同窗更加明確線程池的運行規則,規避資源耗盡的風險。 說明:Executors 返回的線程池對象的弊端以下:
1)FixedThreadPool 和 SingleThreadPool: 容許的請求隊列長度爲 Integer.MAX_VALUE,可能會堆積大量的請求,從而致使 OOM。
2)CachedThreadPool 和 ScheduledThreadPool: 容許的建立線程數量爲 Integer.MAX_VALUE,可能會建立大量的線程,從而致使 OOM。
【點評】規則好,嚴格遵循。
分佈式程序下面,更應該考慮自定義的線程池。
package com.test; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * Java線程:線程池-自定義線程池 * * @author Administrator 2009-11-4 23:30:44 */ public class NewTest { public static void main(String[] args) { // 建立等待隊列 BlockingQueue bqueue = new ArrayBlockingQueue(20); // 建立一個單線程執行程序,它可安排在給定延遲後運行命令或者按期地執行。 ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 10, 50, TimeUnit.MILLISECONDS, bqueue); // 建立實現了Runnable接口對象,Thread對象固然也實現了Runnable接口 Thread t1 = new MyThread("test1"); Thread t2 = new MyThread("test2"); Thread t3 = new MyThread("test3"); Thread t4 = new MyThread("test4"); Thread t5 = new MyThread("test5"); Thread t6 = new MyThread("test6"); Thread t7 = new MyThread(); // 將線程放入池中進行執行 pool.execute(t1); pool.execute(t2); pool.execute(t3); pool.execute(t4); pool.execute(t5); pool.execute(t6); pool.execute(t7); int count= pool.getActiveCount(); System.out.println(pool.isTerminated()); do{ System.out.println(count); try { Thread.sleep(500L); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } count= pool.getActiveCount(); } while(count>0); System.out.println(pool.isTerminated()); pool.shutdown(); System.out.println(pool.isTerminating()); try { Thread.sleep(2000L); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(pool.isTerminated()); } } class MyThread extends Thread { private String labelName; public MyThread() { } public MyThread(String labelName) { this.labelName=labelName; } @Override public void run() { System.out.println(Thread.currentThread().getName() + " "+this.labelName+"正在執行。。。"); try { Thread.sleep(100L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " "+this.labelName+" end"); } }
5. 【強制】SimpleDateFormat 是線程不安全的類,通常不要定義爲 static 變量,若是定義爲 static,必須加鎖,或者使用 DateUtils 工具類。
正例:注意線程安全,使用 DateUtils。亦推薦以下處理:
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd"); } };
說明:若是是 JDK8 的應用,可使用 Instant 代替 Date,LocalDateTime 代替 Calendar, DateTimeFormatter代替Simpledateformatter,
官方給出的解釋:simple beautiful strong immutable thread-safe。
【點評】規則好,嚴格遵循。 每次都實例化DateFormat。
6. 【強制】高併發時,同步調用應該去考量鎖的性能損耗。能用無鎖數據結構,就不要用鎖;能鎖區塊,就不要鎖整個方法體;能用對象鎖,就不要用類鎖。 說明:儘量使加鎖的代碼塊工做量儘量的小,避免在鎖代碼塊中調用 RPC 方法。
【點評】規則好,嚴格遵循。
7. 【強制】對多個資源、數據庫表、對象同時加鎖時,須要保持一致的加鎖順序,不然可能會造 成死鎖。 說明:線程一須要對錶 A、B、C 依次所有加鎖後才能夠進行更新操做,那麼線程二的加鎖順序 也必須是 A、B、C,不然可能出現死鎖。
【點評】規則好,嚴格遵循。
8. 【強制】併發修改同一記錄時,避免更新丟失,須要加鎖。要麼在應用層加鎖,要麼在緩存加 鎖,要麼在數據庫層使用樂觀鎖,使用 version 做爲更新依據。 說明:若是每次訪問衝突機率小於 20%,推薦使用樂觀鎖,不然使用悲觀鎖。樂觀鎖的重試次 數不得小於 3 次。
【點評】規則好,嚴格遵循。更好的方式是無鎖編程 。表結構以下
Id |
Name |
Count 庫存 |
1 |
火腿 |
50 |
2 |
雨傘 |
40 |
用update table set count=?-1 where id=2 and count=? ?爲傳入參數。這樣能夠無鎖編程。
或者update table set count=?-1 where id=2 and count>0 防止超賣。
9. 【強制】多線程並行處理定時任務時,Timer 運行多個 TimeTask 時,只要其中之一沒有捕獲 拋出的異常,其它任務便會自動終止運行,使用 ScheduledExecutorService 則沒有這個問題。
【點評】規則好,嚴格遵循。
10. 【推薦】使用 CountDownLatch 進行異步轉同步操做,每一個線程退出前必須調用 countDown 方法,線程執行代碼注意 catch 異常,確保 countDown 方法能夠執行,避免主線程沒法執行 至 await 方法,直到超時才返回結果。 說明:注意,子線程拋出異常堆棧,不能在主線程 try-catch 到。
【點評】規則好,嚴格遵循。
11. 【推薦】避免 Random 實例被多線程使用,雖然共享該實例是線程安全的,但會因競爭同一 seed 致使的性能降低。
說明:Random 實例包括 java.util.Random 的實例或者 Math.random()的方式。 正例:在 JDK7 以後,能夠直接使用 API ThreadLocalRandom,而在 JDK7 以前,須要編碼保 證每一個線程持有一個實例。
【點評】規則好,嚴格遵循。
12. 【推薦】在併發場景下,經過雙重檢查鎖(double-checked locking)實現延遲初始化的優 化問題隱患(可參考 The "Double-Checked Locking is Broken" Declaration),推薦問 題解決方案中較爲簡單一種(適用於 JDK5 及以上版本),將目標屬性聲明爲 volatile 型。
反例: class Foo {
private Helper helper = null;
public Helper getHelper() { if (helper == null) synchronized(this) { if (helper == null) helper = new Helper(); } return helper; } // other functions and members... }
【點評】規則好,嚴格遵循。
public class Singleton { private static volatile Singleton instance = null; private Singleton(){} public static Singleton getInstance() { if(instance == null) { synchronized(Singleton.class) { if(instance == null) { instance = new Singleton(); } } } return instance; } }
13. 【參考】volatile 解決多線程內存不可見問題。對於一寫多讀,是能夠解決變量同步問題, 可是若是多寫,一樣沒法解決線程安全問題。若是是 count++操做,使用以下類實現: AtomicInteger count = new AtomicInteger(); count.addAndGet(1); 若是是 JDK8,推 薦使用 LongAdder 對象,比 AtomicLong 性能更好(減小樂觀鎖的重試次數)。
【點評】規則好,嚴格遵循。
14. 【參考】 HashMap 在容量不夠進行 resize 時因爲高併發可能出現死鏈,致使 CPU 飆升,在 開發過程當中可使用其它數據結構或加鎖來規避此風險。
【點評】規則好,嚴格遵循。
15. 【參考】ThreadLocal 沒法解決共享對象的更新問題,ThreadLocal 對象建議使用 static 修飾。這個變量是針對一個線程內全部操做共有的,因此設置爲靜態變量,全部此類實例共享 此靜態變量 ,也就是說在類第一次被使用時裝載,只分配一塊存儲空間,全部此類的對象(只 要是這個線程內定義的)均可以操控這個變量。
【點評】規則好,嚴格遵循。
(九) 其它
1. 【強制】在使用正則表達式時,利用好其預編譯功能,能夠有效加快正則匹配速度。 說明:不要在方法體內定義:Pattern pattern = Pattern.compile(規則);
【點評】規則好,需嚴格遵循。
2. 【強制】velocity 調用 POJO 類的屬性時,建議直接使用屬性名取值便可,模板引擎會自動按 規範調用 POJO 的 getXxx(),若是是 boolean 基本數據類型變量(boolean 命名不須要加 is 前綴),會自動調用 isXxx()方法。 說明:注意若是是 Boolean 包裝類對象,優先調用 getXxx()的方法。
【點評】規則好,需嚴格遵循。
3. 【強制】後臺輸送給頁面的變量必須加$!{var}——中間的感嘆號。 說明:若是 var=null 或者不存在,那麼${var}會直接顯示在頁面上。
【點評】規則好,需嚴格遵循。
4. 【強制】注意 Math.random() 這個方法返回是 double 類型,注意取值的範圍 0≤x<1(可以 取到零值,注意除零異常),若是想獲取整數類型的隨機數,不要將 x 放大 10 的若干倍而後取整,直接使用 Random 對象的 nextInt 或者 nextLong 方法。
【點評】規則好,嚴格遵循。
5. 【強制】獲取當前毫秒數 System.currentTimeMillis(); 而不是 new Date().getTime(); 說明:若是想獲取更加精確的納秒級時間值,使用 System.nanoTime()的方式。 在 JDK8 中, 針對統計時間等場景,推薦使用 Instant 類。
【點評】規則好,嚴格遵循。
6. 【推薦】不要在視圖模板中加入任何複雜的邏輯。 說明:根據 MVC 理論,視圖的職責是展現,不要搶模型和控制器的活。
【點評】規則好,嚴格遵循。
7. 【推薦】任何數據結構的構造或初始化,都應指定大小,避免數據結構無限增加吃光內存。
【點評】規則好,未嚴格遵循。應考慮使用頻率等。
8. 【推薦】對於「明確中止使用的代碼和配置」,如方法、變量、類、配置文件、動態配置屬性 等要堅定從程序中清理出去,避免形成過多垃圾。
【點評】規則好,嚴格遵循。