阿里巴巴java開發規範

1、編程規約

(一) 命名規約

1. 【強制】全部編程相關命名均不能如下劃線或美圓符號開始,也不能如下劃線或美圓符號結束。html

反例: _name / __name / $Object / name_ / name$ / Object$java

凡是以兩個或一個下劃線開始,後面緊跟着一個大寫字母的標識符,無論它出如今哪裏,都是保留給編譯程序或標準庫函數使用的。
此外,凡是以一個下劃線開始,後面無論跟着什麼內容的標識符,若是它出如今文件範圍內(即它不是出如今一個函數內),那麼它也是被保留的。 
若是你用一個保留的標識符來做一個變量的名稱,結果是沒有定義的(程序可能沒法編譯,或者能夠編譯但會崩潰)。
即便你能很是幸運地找到一個目前尚未被你的編譯程序或函數庫使用的標識符,你也應該記住這樣的標識符是保留起來供未來使用的。
所以,最好仍是避免使用如下劃線開始的變量名或函數名。

 

2. 【強制】全部編程相關的命名嚴禁使用拼音與英文混合的方式,更不容許直接使用中文的方式。linux

說明:正確的英文拼寫和語法可讓閱讀者易於理解,避免歧義。注意,即便純拼音命名方式也要避免採用。程序員

反例: DaZhePromotion [打折] / getPingfenByName() [評分] / int 變量 = 3;正則表達式

正例: ali / alibaba / taobao / cainiao / aliyun / youku / hangzhou 等國際通用的名稱,可視爲英文。spring

3. 【強制】類名使用 UpperCamelCase風格,必須聽從駝峯形式,但如下情形例外:(領域模型的相關命名)DO / DTO / VO / DAO等。數據庫

正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion編程

反例:macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotionwindows

4. 【強制】方法名、參數名、成員變量、局部變量都統一使用 lowerCamelCase風格,必須聽從駝峯形式。設計模式

正例: localValue / getHttpMessage() / inputUserId

5. 【強制】常量命名所有大寫,單詞間用下劃線隔開,力求語義表達完整清楚,不要嫌名字長。

正例: MAX_STOCK_COUNT

反例: MAX_COUNT

6. 【強制】抽象類命名使用 Abstract或 Base開頭;異常類命名使用 Exception結尾;測試類命名以它要測試的類的名稱開始,以 Test結尾。

7. 【強制】中括號是數組類型的一部分,數組定義以下:String[] args;

反例:請勿使用 String args[]的方式來定義

8. 【強制】POJO類中的任何布爾類型的變量,都不要加 is,不然部分框架解析會引發序列化錯誤。

反例:定義爲基本數據類型 boolean isSuccess;的屬性,它的方法也是 isSuccess(),RPC框架在反向解析的時候,「覺得」對應的屬性名稱是 success,致使屬性獲取不到,進而拋出異常。

9. 【強制】包名統一使用小寫,點分隔符之間有且僅有一個天然語義的英語單詞。包名統一使用單數形式,可是類名若是有複數含義,類名可使用複數形式。

正例: 應用工具類包名爲 com.alibaba.mpp.util、類名爲 MessageUtils(此規則參考 spring的框架結構)

10.【強制】杜絕徹底不規範的縮寫,避免望文不知義。

反例:<某業務代碼>AbstractClass「縮寫」命名成 AbsClass;condition「縮寫」命名成condi,此類隨意縮寫嚴重下降了代碼的可閱讀性。

11.【推薦】若是使用到了設計模式,建議在類名中體現出具體模式。

說明:將設計模式體如今名字中,有利於閱讀者快速理解架構設計思想。

正例:public class OrderFactory;

public class LoginProxy;

public class ResourceObserver;

12.【推薦】接口類中的方法和屬性不要加任何修飾符號(public 也不要加),保持代碼的簡潔性,並加上有效的 javadoc註釋。儘可能不要在接口裏定義變量,若是必定要定義變量,確定是與接口方法相關,而且是整個應用的基礎常量。

正例:接口方法簽名:void f();

接口基礎常量表示:String COMPANY = "alibaba";

反例:接口方法定義:public abstract void f();

說明:JDK8中接口容許有默認實現,那麼這個 default方法,是對全部實現類都有價值的默認實現。

13.接口和實現類的命名有兩套規則:

1)【強制】對於 Service和 DAO類,基於 SOA的理念,暴露出來的服務必定是接口,內部的實現類用 Impl的後綴與接口區別。

正例:CacheServiceImpl實現 CacheService接口。

2)【推薦】 若是是形容能力的接口名稱,取對應的形容詞作接口名(一般是–able的形式)。

正例:AbstractTranslator實現 Translatable。

14.【參考】枚舉類名建議帶上 Enum後綴,枚舉成員名稱須要全大寫,單詞間用下劃線隔開。

說明:枚舉其實就是特殊的常量類,且構造方法被默認強制是私有。

正例:枚舉名字:DealStatusEnum;成員名稱:SUCCESS / UNKOWN_REASON。

15.【參考】各層命名規約:

A) Service/DAO層方法命名規約

1) 獲取單個對象的方法用 get作前綴。

2) 獲取多個對象的方法用 list作前綴。

3) 獲取統計值的方法用 count作前綴。

4) 插入的方法用 save(推薦)或 insert作前綴。

5) 刪除的方法用 remove(推薦)或 delete作前綴。

6) 修改的方法用 update作前綴。

B) 領域模型命名規約

1) 數據對象:xxxDO,xxx即爲數據表名。

2) 數據傳輸對象:xxxDTO,xxx爲業務領域相關的名稱。

3) 展現對象:xxxVO,xxx通常爲網頁名稱。

4) POJO是 DO/DTO/BO/VO的統稱,禁止命名成 xxxPOJO。

(二) 常量定義

1. 【強制】不容許出現任何魔法值(即未經定義的常量)直接出如今代碼中。

反例: String key="Id#taobao_"+tradeId;

cache.put(key, value);

2. 【強制】long或者 Long初始賦值時,必須使用大寫的 L,不能是小寫的 l,小寫容易跟數字 1混淆,形成誤解。

說明:Long a = 2l; 寫的是數字的 21,仍是 Long型的 2?

3. 【推薦】不要使用一個常量類維護全部常量,應該按常量功能進行歸類,分開維護。如:緩存相關的常量放在類:CacheConsts下;系統配置相關的常量放在類:ConfigConsts下。

說明:大而全的常量類,非得 ctrl+f才定位到修改的常量,不利於理解,也不利於維護。

4. 【推薦】常量的複用層次有五層:跨應用共享常量、應用內共享常量、子工程內共享常量、包內共享常量、類內共享常量。

1) 跨應用共享常量:放置在二方庫中,一般是 client.jar中的 const目錄下。

2) 應用內共享常量:放置在一方庫的 modules中的 const目錄下。

反例:易懂變量也要統必定義成應用內共享常量,兩位攻城師在兩個類中分別定義了表示「是」的變量:

類 A中:public static final String YES = "yes";

類 B中:public static final String YES = "y";

A.YES.equals(B.YES),預期是 true,但實際返回爲 false,致使產生線上問題。

3) 子工程內部共享常量:即在當前子工程的 const目錄下。

4) 包內共享常量:即在當前包下單獨的 const目錄下。

5) 類內共享常量:直接在類內部 private static final定義。

5. 【推薦】若是變量值僅在一個範圍內變化用 Enum類。若是還帶有名稱以外的延伸屬性,必須使用 Enum類,下面正例中的數字就是延伸信息,表示星期幾。

正例:public Enum{ MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4), FRIDAY(5),SATURDAY(6), SUNDAY(7);}

(三) 格式規約

1. 【強制】大括號的使用約定。若是是大括號內爲空,則簡潔地寫成{}便可,不須要換行;若是是非空代碼塊則:

1) 左大括號前不換行。

2) 左大括號後換行。

3) 右大括號前換行。

4) 右大括號後還有 else等代碼則不換行;表示終止右大括號後必須換行。

2. 【強制】 左括號和後一個字符之間不出現空格;一樣,右括號和前一個字符之間也不出現空格。詳見第 5條下方正例提示。

3. 【強制】if/for/while/switch/do等保留字與左右括號之間都必須加空格。

4. 【強制】任何運算符左右必須加一個空格。

說明:運算符包括賦值運算符=、邏輯運算符&&、加減乘除符號、三目運行符等。

5. 【強制】代碼塊縮進 4個空格,若是使用 tab縮進,請設置成 1個 tab爲 4個空格。

正例: (涉及 1-5點)

複製代碼
public static void main(String args[]) {
  // 縮進 4個空格
  String say = "hello";
  // 運算符的左右必須有一個空格
  int flag = 0;
  // 關鍵詞 if與括號之間必須有一個空格,括號內 f與左括號,1與右括號不須要空格
  if (flag == 0) {
    System.out.println(say);
  }
  // 左大括號前加空格且不換行;左大括號後換行
  if (flag == 1) {
    System.out.println("world");
    // 右大括號前換行,右大括號後有 else,不用換行
  } else {
    System.out.println("ok");
    // 右大括號作爲結束,必須換行
  }
}
複製代碼

6. 【強制】單行字符數限制不超過 120個,超出須要換行,換行時,遵循以下原則:

1) 換行時相對上一行縮進 4個空格。

2) 運算符與下文一塊兒換行。

3) 方法調用的點符號與下文一塊兒換行。

4) 在多個參數超長,逗號後進行換行。

5) 在括號前不要換行,見反例。

正例:

StringBuffer sb = new StringBuffer();
//超過 120個字符的狀況下,換行縮進 4個空格,而且方法前的點符號一塊兒換行
sb.append("zi").append("xin")…
  .append("huang");

反例:

複製代碼
StringBuffer sb = new StringBuffer();
//超過 120個字符的狀況下,不要在括號前換行
sb.append("zi").append("xin")…append
("huang"); //參數不少的方法調用也超過 120個字符,逗號後纔是換行處 method(args1, args2, args3, ...
, argsX);
複製代碼

7. 【強制】方法參數在定義和傳入時,多個參數逗號後邊必須加空格。

正例:下例中實參的"a",後邊必需要有一個空格。

method("a", "b", "c");

8. 【推薦】沒有必要增長若干空格來使某一行的字符與上一行的相應字符對齊。

正例:

int a = 3;

long b = 4L;

float c = 5F;

StringBuffer sb = new StringBuffer();

說明:增長 sb這個變量,若是須要對齊,則給 a、b、c都要增長几個空格,在變量比較多的狀況下,是一種累贅的事情。

9. 【強制】IDE的 text file encoding設置爲 UTF-8; IDE中文件的換行符使用 Unix格式,不要使用 windows格式。

CR : Carriage Return 回車   LF: linefeed 換行

<換行>即\n(LF)  <回車><換行>\r\n (CR\LF)
\r 十進制ASCII代碼是13, 十六進制代碼爲0x0d
\n 十進制ASCII代碼是10, 十六製爲0x0a 
windows中的換行符是\r\n 先回車再換行
linux/unix下的換行符是\n

推薦博客:

http://www.cnblogs.com/dartagnan/archive/2010/12/14/2003499.html

http://www.cnblogs.com/DreamDrive/p/6887926.html

10.【推薦】方法體內的執行語句組、變量的定義語句組、不一樣的業務邏輯之間或者不一樣的語義之間插入一個空行。相同業務邏輯和語義之間不須要插入空行。

說明:沒有必要插入多行空格進行隔開。

(四) OOP規約

1. 【強制】避免經過一個類的對象引用訪問此類的靜態變量或靜態方法,無謂增長編譯器解析成本,直接用類名來訪問便可。

2. 【強制】全部的覆寫方法,必須加@Override註解。

反例:getObject()與 get0bject()的問題。一個是字母的 O,一個是數字的 0,加@Override能夠準確判斷是否覆蓋成功。另外,若是在抽象類中對方法簽名進行修改,其實現類會立刻編譯報錯。

3. 【強制】相同參數類型,相同業務含義,纔可使用 Java的可變參數,避免使用 Object。

說明:可變參數必須放置在參數列表的最後。(提倡同窗們儘可能不用可變參數編程)

正例:public User getUsers(String type, Integer... ids);

4. 【強制】對外暴露的接口簽名,原則上不容許修改方法簽名,避免對接口調用方產生影響。接口過期必須加@Deprecated註解,並清晰地說明採用的新接口或者新服務是什麼。

5. 【強制】不能使用過期的類或方法。

說明:java.net.URLDecoder 中的方法 decode(String encodeStr) 這個方法已通過時,應該使用雙參數 decode(String source, String encode)。接口提供方既然明確是過期接口,那麼有義務同時提供新的接口;做爲調用方來講,有義務去考證過期方法的新實現是什麼。

6. 【強制】Object的 equals方法容易拋空指針異常,應使用常量或肯定有值的對象來調用 equals。

正例: "test".equals(object);

反例: object.equals("test");

說明:推薦使用 java.util.Objects#equals (JDK7引入的工具類)

7. 【強制】全部的相同類型的包裝類對象之間值的比較,所有使用 equals方法比較。

說明:對於 Integervar=?在-128至 127之間的賦值,Integer對象是在 IntegerCache.cache產生,會複用已有對象,這個區間內的 Integer值能夠直接使用==進行判斷,可是這個區間以外的全部數據,都會在堆上產生,並不會複用已有對象,這是一個大坑,推薦使用 equals方法進行判斷。

複製代碼
class  A{
    public static void main(String[] args) {
        Integer a = 128, b = 128;
        System.out.println(a == b);//返回false
        Integer c = 127, d = 127;
        System.out.println(c == d);//返回true
    }
}
複製代碼

推薦博客:

http://www.open-open.com/lib/view/open1482374807208.html

8. 【強制】關於基本數據類型與包裝數據類型的使用標準以下:

1) 全部的 POJO類屬性必須使用包裝數據類型。

2) RPC方法的返回值和參數必須使用包裝數據類型。

3) 全部的局部變量推薦使用基本數據類型。

說明:POJO類屬性沒有初值是提醒使用者在須要使用時,必須本身顯式地進行賦值,任何NPE問題,或者入庫檢查,都由使用者來保證。

正例:數據庫的查詢結果多是 null,由於自動拆箱,用基本數據類型接收有 NPE風險。

反例:某業務的交易報表上顯示成交總額漲跌狀況,即正負 x%,x爲基本數據類型,調用的RPC服務,調用不成功時,返回的是默認值,頁面顯示:0%,這是不合理的,應該顯示成中劃

線-。因此包裝數據類型的 null值,可以表示額外的信息,如:遠程調用失敗,異常退出。

9. 【強制】定義 DO/DTO/VO等 POJO類時,不要設定任何屬性默認值。

反例:某業務的 DO的 gmtCreate默認值爲 new Date();可是這個屬性在數據提取時並無置入具體值,在更新其它字段時又附帶更新了此字段,致使建立時間被修改爲當前時間。

10.【強制】序列化類新增屬性時,請不要修改 serialVersionUID字段,避免反序列失敗;若是徹底不兼容升級,避免反序列化混亂,那麼請修改 serialVersionUID值。

說明:注意 serialVersionUID不一致會拋出序列化運行時異常。

11.【強制】構造方法裏面禁止加入任何業務邏輯,若是有初始化邏輯,請放在 init方法中。

12.【強制】POJO類必須寫 toString方法。使用工具類 source> generate toString時,若是繼承了另外一個 POJO類,注意在前面加一下 super.toString。

說明:在方法執行拋出異常時,能夠直接調用 POJO的 toString()方法打印其屬性值,便於排查問題。

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 data + 100;
  } else {
    return data - 100;
  }
}
複製代碼

 17.【推薦】循環體內,字符串的聯接方式,使用 StringBuilder的 append方法進行擴展。

 反例:

String str = "start";
for(int i=0; i<100; i++){
  str = str + "hello";
}

說明:反編譯出的字節碼文件顯示每次循環都會 new出一個 StringBuilder對象,而後進行append操做,最後經過 toString方法返回 String對象,形成內存資源浪費。

18.【推薦】final可提升程序響應效率,聲明成 final的狀況:

1) 不須要從新賦值的變量,包括類屬性、局部變量。

2) 對象參數前加 final,表示不容許修改引用的指向。

3) 類方法肯定不容許被重寫。

19.【推薦】慎用 Object的 clone方法來拷貝對象。

說明:對象的 clone方法默認是淺拷貝,若想實現深拷貝須要重寫 clone方法實現屬性對象的拷貝。

推薦博客:

http://blog.csdn.net/zhangjg_blog/article/details/18369201

http://www.cnblogs.com/DreamDrive/p/5430479.html

http://www.cnblogs.com/DreamDrive/p/5430981.html

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. 【強制】Map/Set的 key爲自定義對象時,必須重寫 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 異常。

4. 【強制】使用集合轉數組的方法,必須使用集合的 toArray(T[] array),傳入的是類型徹底同樣的數組,大小就是 list.size()。

反例:直接使用 toArray無參方法存在問題,此方法返回值只能是 Object[]類,若強轉其它類型數組將出現 ClassCastException錯誤。

正例:

List<String> list = new ArrayList<String>(2);
list.add("guan");
list.add("bao");
String[] array = new String[list.size()];
array = list.toArray(array);

說明:使用 toArray帶參方法,入參分配的數組空間不夠大時,toArray方法內部將從新分配內存空間,並返回新數組地址;若是數組元素大於實際所需,下標爲[ list.size() ]的數組

元素將被置爲 null,其它數組元素保持原值,所以最好將方法入參數組大小定義與集合元素個數一致。

下面是toArray帶參數和不帶參數的源碼:

複製代碼
public Object[] toArray() { 
   Object[] result = newObject[size];    
   System.arraycopy(elementData,0, result, 0,size);    
   return result;
}
public Object[] toArray(Object a[]){    
  if (a.length <size){
    a =(Object[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(),size);
    System.arraycopy(elementData,0, a, 0,size);
  }
  if (a.length >size) {
    a[size] =null;
    return a;
  }
}
複製代碼

不帶參數的toArray方法,是構造的一個Object數組,而後進行數據拷貝,此時進行轉型就會產生ClassCastException

而帶參數的toArray方法,則是根據參數數組的類型,構造了一個對應類型的,長度跟ArrayList的size一致的空數組,雖然方法自己仍是以Object數組的形式返回結果,

不過因爲構造數組使用的ComponentType跟須要轉型的ComponentType一致,就不會產生轉型異常 正確的方式
1. Long[] l = (Long []) list.toArray(new Long[0]);

2. Long [] a = new Long[<totalsize>];
Long [] l =(Long []) list.toArray(a);
第2個要注意的是:你要是傳入的參數爲9個大小,而list裏面有5個object,那麼其餘的四個極可能是null ,使用的時候要注意。

推薦博客:

http://www.cnblogs.com/DreamDrive/p/5626076.html

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)也會隨之修改。

複製代碼
public class Test {
    public static void main(String[] args) {
        String[] str = new String[] { "a", "b" };
        List list = Arrays.asList(str);
        list.add("c");//報錯:java.lang.UnsupportedOperationException
        str[0]= "gujin";
        System.out.println(list);//[gujin, b]
    }
}
複製代碼

推薦博客:

http://www.cnblogs.com/DreamDrive/p/5641065.html

http://www.cnblogs.com/DreamDrive/p/5641191.html

6. 【強制】泛型通配符<?extendsT>來接收返回的數據,此寫法的泛型集合不能使用 add方法。

說明:蘋果裝箱後返回一個<? extends Fruits>對象,此對象就不能往裏加任何水果,包括蘋果。

7. 【強制】不要在 foreach循環裏進行元素的 remove/add操做。remove元素請使用 Iterator方式,若是併發操做,須要對 Iterator對象加鎖。

反例:

複製代碼
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」,會是一樣的結果嗎?

如上 是"1" 打印a [2]

若是是"2" 報錯:java.util.ConcurrentModificationException

正例:

複製代碼
Iterator<String> it = a.iterator();
while(it.hasNext()){
  String temp = it.next();   if(刪除元素的條件){     it.remove();   } }
複製代碼

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. 【推薦】集合初始化時,儘可能指定集合初始值大小。

說明:ArrayList儘可能使用 ArrayList(int initialCapacity) 初始化。

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值組合集合。

11.【推薦】高度注意 Map類集合 K/V能不能存儲 null值的狀況,以下表格:

反例:不少同窗認爲 ConcurrentHashMap是能夠置入 null值。在批量翻譯場景中,子線程分發時,出現置入 null值的狀況,但主線程沒有捕獲到此異常,致使排查困難。

12.【參考】合理利用好集合的有序性(sort)和穩定性(order),避免集合的無序性(unsort)和不穩定性(unorder)帶來的負面影響。

說明:穩定性指集合每次遍歷的元素次序是必定的。有序性是指遍歷的結果是按某種比較規則依次排列的。如:ArrayList是 order/unsort;HashMap是 unorder/unsort;TreeSet是order/sort。

13.【參考】利用 Set元素惟一的特性,能夠快速對另外一個集合進行去重操做,避免使用 List的contains方法進行遍歷去重操做。

(六) 併發處理

1. 【強制】獲取單例對象要線程安全。在單例對象裏面作操做也要保證線程安全。

說明:資源驅動類、工具類、單例工廠類都須要注意。

2. 【強制】線程資源必須經過線程池提供,不容許在應用中自行顯式建立線程。

說明:使用線程池的好處是減小在建立和銷燬線程上所花的時間以及系統資源的開銷,解決資源不足的問題。若是不使用線程池,有可能形成系統建立大量同類線程而致使消耗完內存或者「過分切換」的問題。

3. 【強制】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。

4. 【強制】高併發時,同步調用應該去考量鎖的性能損耗。能用無鎖數據結構,就不要用鎖;能鎖區塊,就不要鎖整個方法體;能用對象鎖,就不要用類鎖。

5. 【強制】對多個資源、數據庫表、對象同時加鎖時,須要保持一致的加鎖順序,不然可能會形成死鎖。

說明:線程一須要對錶 A、B、C依次所有加鎖後才能夠進行更新操做,那麼線程二的加鎖順序也必須是 A、B、C,不然可能出現死鎖。

6. 【強制】併發修改同一記錄時,避免更新丟失,要麼在應用層加鎖,要麼在緩存加鎖,要麼在數據庫層使用樂觀鎖,使用 version做爲更新依據。

說明:若是每次訪問衝突機率小於20%,推薦使用樂觀鎖,不然使用悲觀鎖。樂觀鎖的重試次

數不得小於3次。

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

8. 【強制】線程池不容許使用Executors去建立,而是經過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同窗更加明確線程池的運行規則,規避資源耗盡的風險。

說明:Executors各個方法的弊端:

1)newFixedThreadPool和newSingleThreadExecutor:主要問題是堆積的請求處理隊列可能會耗費很是大的內存,甚至OOM。

2)newCachedThreadPool和newScheduledThreadPool:主要問題是線程數最大數是Integer.MAX_VALUE,可能會建立數量很是多的線程,甚至OOM。

9. 【強制】建立線程或線程池時請指定有意義的線程名稱,方便出錯時回溯。

正例:

public class TimerTaskThread extends Thread {
  public TimerTaskThread(){
  super.setName("TimerTaskThread"); …
}

10.【推薦】使用CountDownLatch進行異步轉同步操做,每一個線程退出前必須調用countDown方法,線程執行代碼注意catch異常,確保countDown方法能夠執行,避免主線程沒法執行至countDown方法,直到超時才返回結果。

說明:注意,子線程拋出異常堆棧,不能在主線程try-catch到。

11.【推薦】避免Random實例被多線程使用,雖然共享該實例是線程安全的,但會因競爭同一seed致使的性能降低。

說明:Random實例包括java.util.Random 的實例或者 Math.random()實例。

正例:在JDK7以後,能夠直接使用APIThreadLocalRandom,在 JDK7以前,能夠作到每一個線程一個實例。

12.【推薦】經過雙重檢查鎖(double-checkedlocking)(在併發場景)實現延遲初始化的優化問題隱患(可參考 The "Double-Checked Locking is Broken" Declaration),推薦問題解決方

案中較爲簡單一種(適用於jdk5及以上版本),將目標屬性聲明爲 volatile型(好比反例中修改helper的屬性聲明爲private volatile Helper helper = null;);

反例:

複製代碼
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...
}
複製代碼

13.【參考】volatile解決多線程內存不可見問題。對於一寫多讀,是能夠解決變量同步問題,可是若是多寫,一樣沒法解決線程安全問題。若是想取回 count++數據,使用以下類實現:

AtomicInteger count = new AtomicInteger(); count.addAndGet(1); count++操做若是是JDK8,推薦使用 LongAdder對象,比 AtomicLong性能更好(減小樂觀鎖的重試次數)。

14.【參考】注意 HashMap的擴容死鏈,致使 CPU飆升的問題。

15.【參考】ThreadLocal沒法解決共享對象的更新問題,ThreadLocal對象建議使用 static修飾。這個變量是針對一個線程內全部操做共有的,因此設置爲靜態變量,全部此類實例共享此靜態

變量 ,也就是說在類第一次被使用時裝載,只分配一塊存儲空間,全部此類的對象(只要是這個線程內定義的)均可以操控這個變量。

(七) 控制語句

1. 【強制】在一個 switch塊內,每一個 case要麼經過 break/return來終止,要麼註釋說明程序將繼續執行到哪個 case爲止;在一個 switch塊內,都必須包含一個 default語句而且放在最後,即便它什麼代碼也沒有。

2. 【強制】在 if/else/for/while/do語句中必須使用大括號,即便只有一行代碼,避免使用下面的形式:if (condition) statements;

3. 【推薦】推薦儘可能少用 else, if-else的方式能夠改寫成:

if(condition){
…
return obj;
}

// 接着寫 else的業務邏輯代碼;

說明:若是使用要 if-elseif-else方式表達邏輯,【強制】請勿超過 3層,超過請使用狀態設計模式。

4. 【推薦】除經常使用方法(如 getXxx/isXxx)等外,不要在條件判斷中執行復雜的語句,以提升可讀性。

正例:

//僞代碼以下

InputStream stream = file.open(fileName, "w");
if (stream != null) {
…
}

反例:

if (file.open(fileName, "w") != null)) {
…
}

5. 【推薦】循環體中的語句要考量性能,如下操做盡可能移至循環體外處理,如定義對象、變量、獲取數據庫鏈接,進行沒必要要的 try-catch操做(這個 try-catch是否能夠移至循環體外)。

6. 【推薦】接口入參保護,這種場景常見的是用於作批量操做的接口。

7. 【參考】方法中須要進行參數校驗的場景:

1) 調用頻次低的方法。

2) 執行時間開銷很大的方法,參數校驗時間幾乎能夠忽略不計,但若是由於參數錯誤致使

中間執行回退,或者錯誤,那得不償失。

3) 須要極高穩定性和可用性的方法。

4) 對外提供的開放接口,無論是 RPC/API/HTTP接口。

8. 【參考】方法中不須要參數校驗的場景:

1) 極有可能被循環調用的方法,不建議對參數進行校驗。但在方法說明裏必須註明外部參數檢查。

2) 底層的方法調用頻度都比較高,通常不校驗。畢竟是像純淨水過濾的最後一道,參數錯誤不太可能到底層纔會暴露問題。通常 DAO層與 Service層都在同一個應用中,部署在同一臺服務器中,因此 DAO的參數校驗,能夠省略。

3) 被聲明成 private只會被本身代碼所調用的方法,若是可以肯定調用方法的代碼傳入參

數已經作過檢查或者確定不會有問題,此時能夠不校驗參數。

(八) 註釋規約

1. 【強制】類、類屬性、類方法的註釋必須使用 javadoc規範,使用/**內容*/格式,不得使用//xxx方式。

說明:在 IDE編輯窗口中,javadoc方式會提示相關注釋,生成 javadoc能夠正確輸出相應註釋;在 IDE中,工程調用方法時,不進入方法便可懸浮提示方法、參數、返回值的意義,提升閱讀效率。

2. 【強制】全部的抽象方法(包括接口中的方法)必需要用 javadoc註釋、除了返回值、參數、異常說明外,還必須指出該方法作什麼事情,實現什麼功能。

說明:若有實現和調用注意事項,請一併說明。 

3. 【強制】全部的類都必須添加建立者信息。

4. 【強制】方法內部單行註釋,在被註釋語句上方另起一行,使用//註釋。方法內部多行註釋使用/* */註釋,注意與代碼對齊。

5. 【強制】全部的枚舉類型字段必需要有註釋,說明每一個數據項的用途。

6. 【推薦】與其「半吊子」英文來註釋,不如用中文註釋把問題說清楚。專有名詞、關鍵字,保

持英文原文便可。

反例:「TCP 鏈接超時」解釋成「傳輸控制協議鏈接超時」,理解反而費腦筋。

7. 【推薦】代碼修改的同時,註釋也要進行相應的修改,尤爲是參數、返回值、異常、核心邏輯等的修改。

說明:代碼與註釋更新不一樣步,就像路網與導航軟件更新不一樣步同樣,若是導航軟件嚴重滯後,就失去了導航的意義。

8. 【參考】註釋掉的代碼儘可能要配合說明,而不是簡單的註釋掉。

說明:代碼被註釋掉有兩種可能性:1)後續會恢復此段代碼邏輯。2)永久不用。前者若是沒有備註信息,難以知曉註釋動機。後者建議直接刪掉(代碼倉庫保存了歷史代碼)。

9. 【參考】對於註釋的要求:第1、可以準確反應設計思想和代碼邏輯;第2、可以描述業務含義,使別的程序員可以迅速瞭解到代碼背後的信息。徹底沒有註釋的大段代碼對於閱讀者形同

天書,註釋是給本身看的,即便隔很長時間,也能清晰理解當時的思路;註釋也是給繼任者看的,使其可以快速接替本身的工做。

10.【參考】好的命名、代碼結構是自解釋的,註釋力求精簡準確、表達到位。避免出現註釋的一個極端:過多過濫的註釋,代碼的邏輯一旦修改,修改註釋是至關大的負擔。

反例:

// put elephant into fridge

put(elephant, fridge);

方法名 put,加上兩個有意義的變量名 elephant和 fridge,已經說明了這是在幹什麼,語義清晰的代碼不須要額外的註釋。

11.【參考】特殊註釋標記,請註明標記人與標記時間。注意及時處理這些標記,經過標記掃描,常常清理此類標記。線上故障有時候就是來源於這些標記處的代碼。

1) 待辦事宜(TODO):( 標記人,標記時間,[預計處理時間])

表示須要實現,但目前還未實現的功能。這其實是一個 javadoc的標籤,目前的javadoc尚未實現,但已經被普遍使用。只能應用於類,接口和方法(由於它是一個 javadoc標籤)。

2) 錯誤,不能工做(FIXME):(標記人,標記時間,[預計處理時間])

在註釋中用 FIXME標記某代碼是錯誤的,並且不能工做,須要及時糾正的狀況。

(九) 其它

1. 【強制】在使用正則表達式時,利用好其預編譯功能,能夠有效加快正則匹配速度。

說明:不要在方法體內定義:Pattern pattern = Pattern.compile(規則);

2. 【強制】避免用 Apache Beanutils進行屬性的 copy。

說明:Apache BeanUtils性能較差,可使用其餘方案好比 Spring BeanUtils, CglibBeanCopier。

3. 【強制】velocity調用 POJO類的屬性時,建議直接使用屬性名取值便可,模板引擎會自動按規範調用 POJO的 getXxx(),若是是 boolean基本數據類型變量(注意,boolean命名不須要加 is前綴),會自動調用 isXxx()方法。

說明:注意若是是 Boolean包裝類對象,優先調用 getXxx()的方法。

4. 【強制】後臺輸送給頁面的變量必須加$!{var}——中間的感嘆號。

說明:若是 var=null或者不存在,那麼${var}會直接顯示在頁面上。

5. 【強制】注意 Math.random() 這個方法返回是 double類型,注意取值範圍 0≤x<1(可以取到零值,注意除零異常),若是想獲取整數類型的隨機數,不要將 x放大 10的若干倍而後取整,直接使用 Random對象的 nextInt或者 nextLong方法。

6. 【強制】獲取當前毫秒數:System.currentTimeMillis(); 而不是 new Date().getTime();

說明:若是想獲取更加精確的納秒級時間值,用 System.nanoTime。在 JDK8中,針對統計時間等場景,推薦使用 Instant類。

7. 【推薦】儘可能不要在 vm中加入變量聲明、邏輯運算符,更不要在 vm模板中加入任何複雜的邏輯。

8. 【推薦】任何數據結構的使用都應限制大小。

說明:這點很難徹底作到,但不少次的故障都是由於數據結構自增加,結果形成內存被吃光。

9. 【推薦】對於「明確中止使用的代碼和配置」,如方法、變量、類、配置文件、動態配置屬性等要堅定從程序中清理出去,避免形成過多垃圾。清理這類垃圾代碼是技術氣場,不要有這樣的觀念:「不作不錯,多作多錯」。

相關文章
相關標籤/搜索