阿里巴巴Java開發手冊

一、編程規約

()命名規約

  1. 【強制】 代碼中的命名均不能以下劃線或美元符號開始,也不能以下劃線或美元符號結束。

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

  2. 【強制】 代碼中的命名嚴禁使用拼音與英文混合的方式,更不允許直接使用中文的方式。說明:正確的英文拼寫和語法可以讓閱讀者易於理解,避免歧義。注意,即使純拼音命名方式也要避免採用。
    反例:DaZhePromotion [打折]/ getPingfenByName() [評分]/ int 某變量 = 3正例:alibaba /taobao / youku / hangzhou等國際通用的名稱,可視同英文。

  3. 【強制】類名使用 UpperCamelCase風格,必須遵從駝峯形式,但以下情形例外:(領域模型的相關命名)DO/ BO / DTO /VO等。
    正例:MarcoPolo/ UserDO / XmlService / TcpUdpDeal / TaPromotion反例:macroPolo/ UserDo / XMLService / TCPUDPDeal / TAPromotion

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

    正例:localValue /getHttpMessage() /inputUserId

  5. 【強制】常量命名全部大寫,單詞間用下劃線隔開,力求語義表達完整清楚,不要嫌名字長。正例:MAX_STOCK_COUNT
    反例:MAX_COUNT

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

7.【強制】中括號是數組類型的一部分,數組定義如下:String[]args;反例:請勿使用String args[]的方式來定義。

——禁止用於商業用途,違者必究——1 /34

阿里巴巴 Java開發手冊

8.【強制】POJO類中布爾類型的變量,都不要加is,否則部分框架解析會引起序列化錯誤。反例:定義爲基本數據類型boolean isSuccess;的屬性,它的方法也是isSuccess(),RPC框架在反向解析的時候,以爲對應的屬性名稱是success,導致屬性獲取不到,進而拋出異常。

9.【強制】包名統一使用小寫,點分隔符之間有且僅有一個自然語義的英語單詞。包名統一使用單數形式,但是類名如果有複數含義,類名可以使用複數形式。
正例:應用工具類包名爲com.alibaba.open.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)【強制】對於ServiceDAO類,基於SOA的理念,暴露出來的服務一定是接口,內部

的實現類用Impl的後綴與接口區別。正例:CacheServiceImpl實現CacheService接口。

2)【推薦】 如果是形容能力的接口名稱,取對應的形容詞做接口名(通常是able的形式)正例:AbstractTranslator實現Translatable

14.【參考】枚舉類名建議帶上Enum後綴,枚舉成員名稱需要全大寫,單詞間用下劃線隔開。說明:枚舉其實就是特殊的常量類,且構造方法被默認強制是私有。正例:枚舉名字:DealStatusEnum,成員名稱:SUCCESS/UNKOWN_REASON

——禁止用於商業用途,違者必究——2 /34

阿里巴巴 Java開發手冊

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)POJODO/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?

  1. 【推薦】不要使用一個常量類維護所有常量,應該按常量功能進行歸類,分開維護。如:緩存相關的常量放在類:CacheConsts;系統配置相關的常量放在類:ConfigConsts下。說明:大而全的常量類,非得使用查找功能才能定位到修改的常量,不利於理解和維護。

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

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

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

    A中:public static final String YES= "yes";
    B中:public static final String YES= "y";A.YES.equals(B.YES),預期是true,但實際返回爲false,導致產生線上問題。

——禁止用於商業用途,違者必究——3 /34

阿里巴巴 Java開發手冊
3)子工程內部共享常量:即在當前子工程的constant目錄下。

4)包內共享常量:即在當前包下單獨的constant目錄下。
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字符。

    說明:如果使用tab 縮進,必須設置1 tab 4 個空格。IDEA設置 tab4個空格時,請勿勾選Use tab character

;而在eclipse中,必須勾選

insert spaces for tabs

正例:(涉及1-5)

public static void main(String args[]) {

//縮進4個空格
String say = "hello";
//運算符的左右必須有一個空格
int flag = 0;
//關鍵詞if與括號之間必須有一個空格,括號內的f與左括號,0與右括號不需要空格if (flag == 0) {

          System.out.println(say);
       }

//左大括號前加空格且不換行;左大括號後換行if (flag == 1) {

          System.out.println("world");

//右大括號前換行,右大括號後有else,不用換行——禁止用於商業用途,違者必究——

4 /34

阿里巴巴 Java開發手冊

} 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")...
          .append("huang")...
          .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.【強制】IDEtext file encoding設置爲UTF-8; IDE中文件的換行符使用Unix格式,不要使用windows格式。

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

int a = 3;
long b = 4L;
float c = 5F;
StringBuffer sb = new StringBuffer();

說明:增加sb這個變量,如果需要對齊,則給abc都要增加幾個空格,在變量比較多的情況下,是一種累贅的事情。

——禁止用於商業用途,違者必究——5 /34

阿里巴巴 Java開發手冊
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. 【強制】Objectequals方法容易拋空指針異常,應使用常量或確定有值的對象來調用equals

    正例:"test".equals(object);
    反例:object.equals("test");說明:推薦使用java.util.Objects#equals(JDK7引入的工具類)

  7. 【強制】所有的相同類型的包裝類對象之間值的比較,全部使用equals 方法比較。說明:對於Integer var=?-128127之間的賦值,Integer對象是在IntegerCache.cache產生,會複用已有對象,這個區間內的 Integer值可以直接使用==進行判斷,但是這個區間之外的所有數據,都會在堆上產生,並不會複用已有對象,這是一個大坑,推薦使用equals 方法進行判斷。

——禁止用於商業用途,違者必究——6 /34

阿里巴巴 Java開發手冊
8.【強制】關於基本數據類型與包裝數據類型的使用標準如下:

1)所有的POJO類屬性必須使用包裝數據類型。
2)RPC方法的返回值和參數必須使用包裝數據類型。
3)所有的局部變量【推薦】使用基本數據類型。
說明:POJO類屬性沒有初值是提醒使用者在需要使用時,必須自己顯式地進行賦值,任何

NPE問題,或者入庫檢查,都由使用者來保證。
正例:數據庫的查詢結果可能是null,因爲自動拆箱,用基本數據類型接收有NPE風險。反例:比如顯示成交總額漲跌情況,即正負x%,x爲基本數據類型,調用的RPC服務,調用

不成功時,返回的是默認值,頁面顯示:0%,這是不合理的,應該顯示成中劃線-。所以包裝數據類型的null值,能夠表示額外的信息,如:遠程調用失敗,異常退出。

9.【強制】定義DO/DTO/VOPOJO類時,不要設定任何屬性默認值反例:POJO類的gmtCreate默認值爲new Date();但是這個屬性在數據提取時並沒有置入具體值,在更新其它字段時又附帶更新了此字段,導致創建時間被修改成當前時間。

10.【強制】序列化類新增屬性時,請不要修改serialVersionUID字段,避免反序列失敗;如果完全不兼容升級,避免反序列化混亂,那麼請修改serialVersionUID值。
說明:注意serialVersionUID不一致會拋出序列化運行時異常。

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

12.【強制】POJO類必須寫toString方法。使用IDE的中工具:source>generate toString時,如果繼承了另一個POJO類,注意在前面加一下super.toString說明:在方法執行拋出異常時,可以直接調用POJOtoString()方法打印其屬性值,便於排查問題。

13.【推薦】使用索引訪問用Stringsplit方法得到的數組時,需做最後一個分隔符後有無

內容的檢查,否則會有拋IndexOutOfBoundsException的風險。

說明:

String str = "a,b,c,,";
String[] ary = str.split(",");
//預期大於3,結果是3System.out.println(ary.length);

14.【推薦】當一個類有多個構造方法,或者多個同名方法,這些方法應該按順序放置在一起,便於閱讀。

——禁止用於商業用途,違者必究——7 /34

阿里巴巴 Java開發手冊
15.【推薦】 類內方法定義順序依次是:公有方法或保護方法>私有方法>getter/setter

方法。說明:公有方法是類的調用者和維護者最關心的方法,首屏展示最好;保護方法雖然只是子類關心,也可能是模板設計模式下的核心方法;而私有方法外部一般不需要特別關心,是一個黑盒實現;因爲方法信息價值較低,所有ServiceDAOgetter/setter方法放在類體最後。

16.【推薦】setter方法中,參數名稱與類成員變量名稱一致,this.成員名=參數名。在

getter/setter方法中,儘量不要增加業務邏輯,增加排查問題的難度。

反例:

public Integer getData(){if(true) {

return data + 100;}else {

return data - 100;}

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

反例:

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.【推薦】慎用Objectclone方法來拷貝對象。
說明:對象的clone方法默認是淺拷貝,若想實現深拷貝需要重寫clone方法實現屬性對象的拷貝。

——禁止用於商業用途,違者必究——8 /34

阿里巴巴 Java開發手冊20.【推薦】類成員與方法訪問控制從嚴:

1)如果不允許外部直接通過new來創建對象,那麼構造方法必須是private2)工具類不允許有publicdefault構造方法。
3)類非static成員變量並且與子類共享,必須是protected
4)類非static成員變量並且僅在本類使用,必須是private

5)static成員變量如果僅在本類使用,必須是private6)若是static成員變量,必須考慮是否爲final
7)類成員方法只供類內部調用,必須是private
8)類成員方法只對繼承類公開,那麼限制爲protected

說明:任何類、方法、參數、變量,嚴控訪問範圍。過寬泛的訪問範圍,不利於模塊解耦。思考:如果是一個private的方法,想刪除就刪除,可是一個publicService方法,或者一個public的成員變量,刪除一下,不得手心冒點汗嗎?變量像自己的小孩,儘量在自己的視線內,變量作用域太大,如果無限制的到處跑,那麼你會擔心的。

——禁止用於商業用途,違者必究——9 /34

阿里巴巴 Java開發手冊

()集合處理

1.【強制】關於hashCodeequals的處理,遵循如下規則:
1) 只要重寫equals,就必須重寫hashCode
2) 因爲Set存儲的是不重複的對象,依據hashCodeequals進行判斷,所以Set存儲的對象必須重寫這兩個方法。
3) 如果自定義對象做爲Map的鍵,那麼必須重寫hashCodeequals
正例:String重寫了hashCodeequals方法,所以我們可以非常愉快地使用String對象作爲key來使用。

  1. 【強制】ArrayListsubList結果不可強轉成ArrayList,否則會拋出ClassCastException異常:java.util.RandomAccessSubList cannot be cast to java.util.ArrayList ;說明:subList返回的是 ArrayList的內部類 SubList,並不是ArrayList ,而是ArrayList的一個視圖,對於SubList子列表的所有操作最終會反映到原列表上。

  2. 【強制】 在 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,其它數組元素保持原值,因此最好將方法入參數組大小定義與集合元素個數一致。

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)也會隨之修改。——禁止用於商業用途,違者必究——

10 /34

阿里巴巴 Java開發手冊
6.【強制】泛型通配符<?extends T>來接收返回的數據,此寫法的泛型集合不能使用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”,會是同樣的

結果嗎?

正例:

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;}

}

——禁止用於商業用途,違者必究——

11 /34

阿里巴巴 Java開發手冊9.【推薦】集合初始化時,儘量指定集合初始值大小。

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

10.【推薦】使用entrySet遍歷Map類集合KV,而不是keySet方式進行遍歷。
說明:keySet其實是遍歷了2次,一次是轉爲Iterator對象,另一次是從hashMap中取出key所對應的value。而entrySet只是遍歷了一次就把keyvalue都放到了entry中,效率更高。如果是JDK8,使用Map.foreach方法。
正例:values()返回的是V值集合,是一個list集合對象;keySet()返回的是K值集合,是一個Set集合對象;entrySet()返回的是K-V值組合集合。

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

集合類

Key

Value

Super

說明

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)帶來的負面影響。說明:穩定性指集合每次遍歷的元素次序是一定的。有序性是指遍歷的結果是按某種比較規則依次排列的。如:ArrayListorder/unsort;HashMapunorder/unsort;TreeSetorder/sort

13.【參考】利用Set元素唯一的特性,可以快速對一個集合進行去重操作,避免使用Listcontains方法進行遍歷、對比、去重操作。

()併發處理
1.【強制】獲取單例對象需要保證線程安全,其中的方法也要保證線程安全。

說明:資源驅動類、工具類、單例工廠類都需要注意。
2.【強制】創建線程或線程池時請指定有意義的線程名稱,方便出錯時回溯。

正例:

public class TimerTaskThread extends Thread {public TimerTaskThread(){

super.setName("TimerTaskThread"); ...}

——禁止用於商業用途,違者必究——

12 /34

阿里巴巴 Java開發手冊

  1. 【強制】線程資源必須通過線程池提供,不允許在應用中自行顯式創建線程。

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

  2. 【強制】線程池不允許使用 Executors去創建,而是通過 ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。說明:Executors返回的線程池對象的弊端如下:

    1)FixedThreadPoolSingleThreadPool:
    允許的請求隊列長度爲 Integer.MAX_VALUE,可能會堆積大量的請求,從而導致OOM

    2)CachedThreadPoolScheduledThreadPool:
    允許的創建線程數量爲 Integer.MAX_VALUE,可能會創建大量的線程,從而導致OOM

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 strongimmutable thread-safe

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

  2. 【強制】對多個資源、數據庫表、對象同時加鎖時,需要保持一致的加鎖順序,否則可能會造成死鎖。

    說明:線程一需要對錶ABC依次全部加鎖後纔可以進行更新操作,那麼線程二的加鎖順序也必須是ABC,否則可能出現死鎖。

  3. 【強制】併發修改同一記錄時,避免更新丟失,要麼在應用層加鎖,要麼在緩存加鎖,要麼在數據庫層使用樂觀鎖,使用version 作爲更新依據。
    說明:如果每次訪問衝突概率小於20%,推薦使用樂觀鎖,否則使用悲觀鎖。樂觀鎖的重試次數不得小於3 次。

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

——禁止用於商業用途,違者必究——13 /34

阿里巴巴 Java開發手冊
10.【推薦】使用CountDownLatch進行異步轉同步操作,每個線程退出前必須調用countDown

方法,線程執行代碼注意catch異常,確保countDown方法可以執行,避免主線程無法執行至countDown方法,直到超時才返回結果。說明:注意,子線程拋出異常堆棧,不能在主線程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...

}

13.【參考】volatile解決多線程內存不可見問題。對於一寫多讀,是可以解決變量同步問題,但是如果多寫,同樣無法解決線程安全問題。如果是count++操作,使用如下類實現:AtomicInteger count =new AtomicInteger(); count.addAndGet(1);如果是JDK8,推薦使用LongAdder對象,比AtomicLong性能更好(減少樂觀鎖的重試次數)

14.【參考】HashMap在容量不夠進行resize時由於高併發可能出現死鏈,導致CPU飆升,在開發過程中注意規避此風險。

15.【參考】ThreadLocal無法解決共享對象的更新問題,ThreadLocal對象建議使用static修飾。這個變量是針對一個線程內所有操作共有的,所以設置爲靜態變量,所有此類實例共享此靜態變量 ,也就是說在類第一次被使用時裝載,只分配一塊存儲空間,所有此類的對象(只要是這個線程內定義的)都可以操控這個變量。

——禁止用於商業用途,違者必究——14 /34

阿里巴巴 Java開發手冊

()控制語句

  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()...else if()...else...方式表達邏輯,【強制】請勿超過3層,

    超過請使用狀態設計模式。
    正例:邏輯上超過3 層的if-else 代碼可以使用衛語句,或者狀態模式來實現。

  4. 【推薦】除常用方法(如 getXxx/isXxx)等外,不要在條件判斷中執行其它複雜的語句,將複雜邏輯判斷的結果賦值給一個有意義的布爾變量名,以提高可讀性。
    說明:很多if 語句內的邏輯相當複雜,閱讀者需要分析條件表達式的最終結果,才能明確什麼樣的條件執行什麼樣的語句,那麼,如果閱讀者分析邏輯表達式錯誤呢?

    正例:

    //僞代碼如下
    boolean existed = (file.open(fileName, "w") != null)&& (...) || (...);if (existed) {

    ...}

    反例:

    if ((file.open(fileName, "w") != null) &&(...) || (...)) {...

    }

  5. 【推薦】循環體中的語句要考量性能,以下操作儘量移至循環體外處理,如定義對象、變量、

    獲取數據庫連接,進行不必要的 try-catch操作(這個try-catch是否可以移至循環體外)

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

  7. 【參考】方法中需要進行參數校驗的場景:
    1)調用頻次低的方法。
    2)執行時間開銷很大的方法,參數校驗時間幾乎可以忽略不計,但如果因爲參數錯誤導致

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

——禁止用於商業用途,違者必究——15 /34

阿里巴巴 Java開發手冊3)需要極高穩定性和可用性的方法。

4)對外提供的開放接口,不管是RPC/API/HTTP接口。5) 敏感權限入口。

8.【參考】方法中不需要參數校驗的場景:
1)極有可能被循環調用的方法,不建議對參數進行校驗。但在方法說明裏必須註明外部參

數檢查。
2)底層的方法調用頻度都比較高,一般不校驗。畢竟是像純淨水過濾的最後一道,參數錯

誤不太可能到底層纔會暴露問題。一般DAO層與Service層都在同一個應用中,部署在同一臺服務器中,所以DAO的參數校驗,可以省略。

3)被聲明成private只會被自己代碼所調用的方法,如果能夠確定調用方法的代碼傳入參數已經做過檢查或者肯定不會有問題,此時可以不校驗參數。

()註釋規約

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

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

  2. 【強制】所有的抽象方法(包括接口中的方法)必須要用Javadoc 註釋、除了返回值、參數、異常說明外,還必須指出該方法做什麼事情,實現什麼功能。說明:對子類的實現要求,或者調用注意事項,請一併說明。

  3. 【強制】所有的類都必須添加創建者信息。

  4. 【強制】方法內部單行註釋,在被註釋語句上方另起一行,使用//註釋。方法內部多行註釋

    使用/* */註釋,注意與代碼對齊。

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

  6. 【推薦】與其半吊子英文來註釋,不如用中文註釋把問題說清楚。專有名詞與關鍵字保持英文原文即可。
    反例:TCP連接超時解釋成傳輸控制協議連接超時,理解反而費腦筋。

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

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

——禁止用於商業用途,違者必究——16 /34

阿里巴巴 Java開發手冊

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

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

  2. 【參考】對於註釋的要求:第一、能夠準確反應設計思想和代碼邏輯;第二、能夠描述業務含義,使別的程序員能夠迅速瞭解到代碼背後的信息。完全沒有註釋的大段代碼對於閱讀者形同天書,註釋是給自己看的,即使隔很長時間,也能清晰理解當時的思路;註釋也是給繼任者看的,使其能夠快速接替自己的工作。

10.【參考】好的命名、代碼結構是自解釋的,註釋力求精簡準確、表達到位。避免出現註釋的

   一個極端:過多過濫的註釋,代碼的邏輯一旦修改,修改註釋是相當大的負擔。

反例:

// put elephant into fridge

put(elephant, fridge);
方法名put,加上兩個有意義的變量名elephantfridge,已經說明了這是在幹什麼,語

   義清晰的代碼不需要額外的註釋。

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

1)待辦事宜(TODO):(標記人,標記時間,[預計處理時間])表示需要實現,但目前還未實現的功能。這實際上是一個Javadoc的標籤,目前的Javadoc

還沒有實現,但已經被廣泛使用。只能應用於類,接口和方法(因爲它是一個Javadoc標籤)2)錯誤,不能工作(FIXME):(標記人,標記時間,[預計處理時間])

在註釋中用FIXME標記某代碼是錯誤的,而且不能工作,需要及時糾正的情況。()其它

  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 方法。

——禁止用於商業用途,違者必究——17 /34

阿里巴巴 Java開發手冊
5.【強制】獲取當前毫秒數System.currentTimeMillis();而不是new Date().getTime();

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

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

  2. 【推薦】任何數據結構的構造或初始化,都應指定大小,避免數據結構無限增長吃光內存。

  3. 【推薦】對於明確停止使用的代碼和配置,如方法、變量、類、配置文件、動態配置屬性

     等要堅決從程序中清理出去,避免造成過多垃圾。
    

——禁止用於商業用途,違者必究——18 /34

阿里巴巴 Java開發手冊

二、異常日誌

()異常處理

  1. 【強制】不要捕獲 Java類庫中定義的繼承自 RuntimeException的運行時異常類,如:IndexOutOfBoundsException / NullPointerException,這類異常由程序員預檢查來規避,保證程序健壯性。
    正例:if(obj != null) {...}

    反例:try { obj.method() } catch(NullPointerException e){...}

  2. 【強制】異常不要用來做流程控制,條件控制,因爲異常的處理效率比條件分支低。

  3. 【強制】對大段代碼進行 try-catch,這是不負責任的表現。catch時請分清穩定代碼和非穩定代碼,穩定代碼指的是無論如何不會出錯的代碼。對於非穩定代碼的catch 儘可能進行區分異常類型,再做對應的異常處理。

  4. 【強制】捕獲異常是爲了處理它,不要捕獲了卻什麼都不處理而拋棄之,如果不想處理它,請將該異常拋給它的調用者。最外層的業務使用者,必須處理異常,將其轉化爲用戶可以理解的內容。

  5. 【強制】有 try塊放到了事務代碼中,catch異常後,如果需要回滾事務,一定要注意手動回滾事務。

  6. 【強制】finally塊必須對資源對象、流對象進行關閉,有異常也要做 try-catch說明:如果JDK7,可以使用try-with-resources方式。

  7. 【強制】不能在 finally塊中使用 return,finally塊中的 return返回後方法結束執行,不會再執行 try塊中的 return語句。

  8. 【強制】捕獲異常與拋異常,必須是完全匹配,或者捕獲異常是拋異常的父類。說明:如果預期對方拋的是繡球,實際接到的是鉛球,就會產生意外情況。

  9. 【推薦】方法的返回值可以爲null,不強制返回空集合,或者空對象等,必須添加註釋充分說明什麼情況下會返回null 值。調用方需要進行null 判斷防止NPE 問題。說明:本規約明確防止NPE 是調用者的責任。即使被調用方法返回空集合或者空對象,對調用者來說,也並非高枕無憂,必須考慮到遠程調用失敗,運行時異常等場景返回null 的情況。

10.【推薦】防止NPE,是程序員的基本修養,注意NPE產生的場景:
1)返回類型爲包裝數據類型,有可能是null,返回int值時注意判空。

反例:public int f(){return Integer對象};如果爲null,自動解箱拋NPE2)數據庫的查詢結果可能爲null
3)集合裏的元素即使isNotEmpty,取出的數據元素也可能爲null

——禁止用於商業用途,違者必究——19 /34

阿里巴巴 Java開發手冊4)遠程調用返回對象,一律要求進行NPE判斷。

5)對於Session中獲取的數據,建議NPE檢查,避免空指針。
6)級聯調用obj.getA().getB().getC();一連串調用,易產生NPE

11.【推薦】在代碼中使用拋異常還是返回錯誤碼,對於公司外的http/api開放接口必須使用錯誤碼;而應用內部推薦異常拋出;跨應用間RPC調用優先考慮使用Result方式,封裝isSuccess錯誤碼錯誤簡短信息
說明:關於RPC方法返回方式使用Result方式的理由:

1)使用拋異常返回方式,調用方如果沒有捕獲到就會產生運行時錯誤。

2)如果不加棧信息,只是new自定義異常,加入自己的理解的error message,對於調用端解決問題的幫助不會太多。如果加了棧信息,在頻繁調用出錯的情況下,數據序列化和傳輸的性能損耗也是問題。

12.【推薦】定義時區分unchecked/checked異常,避免直接使用RuntimeException拋出,更不允許拋出Exception或者Throwable,應使用有業務含義的自定義異常。推薦業界已定義過的自定義異常,如:DAOException/ServiceException等。

13.【參考】避免出現重複的代碼(Dont Repeat Yourself),即DRY原則。說明:隨意複製和粘貼代碼,必然會導致代碼的重複,在以後需要修改時,需要修改所有的副本,容易遺漏。必要時抽取共性方法,或者抽象公共類,甚至是共用模塊。正例:一個類中有多個public方法,都需要進行數行相同的參數校驗操作,這個時候請抽取:

private boolean checkParam(DTO dto){...}

()日誌規約
1.【強制】應用中不可直接使用日誌系統(Log4jLogback)中的API,而應依賴使用日誌框架

SLF4J中的API,使用門面模式的日誌框架,有利於維護和各個類的日誌處理方式統一。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Abc.class);

2.【強制】日誌文件推薦至少保存15天,因爲有些異常具備以爲頻次發生的特點。

3.【強制】應用中的擴展日誌(如打點、臨時監控、訪問日誌等)命名方式:appName_logType_logName.loglogType:日誌類型,推薦分類有stats/desc/monitor/visit;logName:日誌描述。這種命名的好處:通過文件名就可知道日誌文件屬於什麼應用,什麼類型,什麼目的,也有利於歸類查找。

正例:mppserver應用中單獨監控時區轉換異常,如:mppserver_monitor_timeZoneConvert.log說明:推薦對日誌進行分類,錯誤日誌和業務日誌儘量分開存放,便於開發人員查看,也便於通過日誌對系統進行及時監控。

——禁止用於商業用途,違者必究——20 /34

阿里巴巴 Java開發手冊

  1. 【強制】對 trace/debug/info級別的日誌輸出,必須使用條件輸出形式或者使用佔位符的方

    式。

    說明:logger.debug("Processing trade with id: " + id+ " symbol: " +symbol);如果日誌級別是warn,上述日誌不會打印,但是會執行字符串拼接操作,如果symbol 是對象,會執行toString()方法,浪費了系統資源,執行了上述操作,最終日誌卻沒有打印。正例:(條件)

    if (logger.isDebugEnabled()) {
    logger.debug("Processing trade with id: " + id + " symbol: " + symbol);

    }

    正例:(佔位符)
    logger.debug("Processing trade with id: {} symbol : {} ", id, symbol);

  2. 【強制】避免重複打印日誌,浪費磁盤空間,務必在 log4j.xml中設置 additivity=false正例:<logger name="com.taobao.dubbo.config" additivity="false">

  3. 【強制】異常信息應該包括兩類信息:案發現場信息和異常堆棧信息。如果不處理,那麼往上拋。

    正例:logger.error(各類參數或者對象toString + "_" + e.getMessage(), e);

  4. 【推薦】可以使用 warn日誌級別來記錄用戶輸入參數錯誤的情況,避免用戶投訴時,無所適從。注意日誌輸出的級別,error級別只記錄系統邏輯出錯、異常等重要的錯誤信息。如非必要,請不要在此場景打出error 級別。

  5. 【推薦】謹慎地記錄日誌。生產環境禁止輸出 debug日誌;有選擇地輸出info 日誌;如果使用warn 來記錄剛上線時的業務行爲信息,一定要注意日誌輸出量的問題,避免把服務器磁盤撐爆,並記得及時刪除這些觀察日誌。說明:大量地輸出無效日誌,不利於系統性能提升,也不利於快速定位錯誤點。記錄日誌時請思考:這些日誌真的有人看嗎?看到這條日誌你能做什麼?能不能給問題排查帶來好處?

——禁止用於商業用途,違者必究——21 /34

阿里巴巴 Java開發手冊

三、MySQL規約()建表規約

  1. 【強制】表達是與否概念的字段,必須使用 is_xxx的方式命名,數據類型是 unsigned tinyint(1表示是,0表示否),此規則同樣適用於odps建表。說明:任何字段如果爲非負數,必須是unsigned

  2. 【強制】表名、字段名必須使用小寫字母或數字;禁止出現數字開頭,禁止兩個下劃線中間只出現數字。數據庫字段名的修改代價很大,因爲無法進行預發佈,所以字段名稱需要慎重考慮。正例:getter_admin,task_config,level3_name反例:GetterAdmin,taskConfig,level_3_name

  3. 【強制】表名不使用複數名詞。說明:表名應該僅僅表示表裏面的實體內容,不應該表示實體數量,對應於DO 類名也是單數形式,符合表達習慣。

  4. 【強制】禁用保留字,如 descrangematchdelayed等,請參考 MySQL官方保留字。

  5. 【強制】唯一索引名爲 uk_字段名;普通索引名則爲idx_字段名。

    說明:uk_unique key;idx_index的簡稱。

  6. 【強制】小數類型爲 decimal,禁止使用float double
    說明:floatdouble在存儲的時候,存在精度損失的問題,很可能在值的比較時,得到不正確的結果。如果存儲的數據範圍超過decimal 的範圍,建議將數據拆成整數和小數分開存儲。

  7. 【強制】如果存儲的字符串長度幾乎相等,使用 char定長字符串類型。

  8. 【強制】varchar是可變長字符串,不預先分配存儲空間,長度不要超過 5000,如果存儲長度大於此值,定義字段類型爲text,獨立出來一張表,用主鍵來對應,避免影響其它字段索引效率。

  9. 【強制】表必備三字段:id,gmt_create,gmt_modified說明:其中id必爲主鍵,類型爲unsigned bigint、單表時自增、步長爲1gmt_create,gmt_modified的類型均爲 date_time類型。

10.【推薦】表的命名最好是加上業務名稱_表的作用正例:tiger_task/tiger_reader/mpp_config

11.【推薦】庫名與應用名稱儘量一致。
12.【推薦】如果修改字段含義或對字段表示的狀態追加時,需要及時更新字段註釋。

——禁止用於商業用途,違者必究——

22 /34

阿里巴巴 Java開發手冊
13.【推薦】字段允許適當冗餘,以提高性能,但是必須考慮數據同步的情況。冗餘字段應遵循:

1)不是頻繁修改的字段。

2)不是varchar超長字段,更不能是text字段。正例:商品類目名稱使用頻率高,字段長度短,名稱基本一成不變,可在相關聯的表中冗餘存儲類目名稱,避免關聯查詢。

14.【推薦】單錶行數超過500萬行或者單表容量超過2GB,才推薦進行分庫分表。說明:如果預計三年後的數據量根本達不到這個級別,請不要在創建表時就分庫分表。

15.【參考】合適的字符存儲長度,不但節約數據庫表空間、節約索引存儲,更重要的是提升檢索速度。

正例:人的年齡用unsigned tinyint(表示範圍0-255,人的壽命不會超過255);海龜就必須是smallint,但如果是太陽的年齡,就必須是int;如果是所有恆星的年齡都加起來,那麼就必須使用bigint

()索引規約

  1. 【強制】業務上具有唯一特性的字段,即使是組合字段,也必須建成唯一索引。說明:不要以爲唯一索引影響了insert 速度,這個速度損耗可以忽略,但提高查找速度是明顯的;另外,即使在應用層做了非常完善的校驗和控制,只要沒有唯一索引,根據墨菲定律,必然有髒數據產生。

  2. 【強制】 超過三個表禁止 join。需要join 的字段,數據類型保持絕對一致;多表關聯查詢時,保證被關聯的字段需要有索引。
    說明:即使雙表join 也要注意表索引、SQL性能。

  3. 【強制】在 varchar字段上建立索引時,必須指定索引長度,沒必要對全字段建立索引,根據實際文本區分度決定索引長度。說明:索引的長度與區分度是一對矛盾體,一般對字符串類型數據,長度爲20 的索引,區分度會高達90%以上,可以使用count(distinct left(列名,索引長度))/count(*)的區分度來確定。

  4. 【強制】頁面搜索嚴禁左模糊或者全模糊,如果需要請走搜索引擎來解決。說明:索引文件具有B-Tree的最左前綴匹配特性,如果左邊的值未確定,那麼無法使用此索引。

  5. 【推薦】如果有 order by的場景,請注意利用索引的有序性order by 最後的字段是組合索引的一部分,並且放在索引組合順序的最後,避免出現file_sort的情況,影響查詢性能。正例:where a=?and b=? order by c; 索引:a_b_c反例:索引中有範圍查找,那麼索引有序性無法利用,如:WHERE a>10 ORDER BY b;索引a_b無法排序。

——禁止用於商業用途,違者必究——23 /34

阿里巴巴 Java開發手冊
6.【推薦】利用覆蓋索引來進行查詢操作,來避免回表操作。

說明:如果一本書需要知道第11章是什麼標題,會翻開第11章對應的那一頁嗎?目錄瀏覽一下就好,這個目錄就是起到覆蓋索引的作用。正例:能夠建立索引的種類:主鍵索引、唯一索引、普通索引,而覆蓋索引是一種查詢的一種效果,用explain的結果,extra列會出現:using index

  1. 【推薦】利用延遲關聯或者子查詢優化超多分頁場景。
    說明:MySQL並不是跳過 offset行,而是取 offset+N行,然後返回放棄前 offset行,返回N行,那當 offset特別大的時候,效率就非常的低下,要麼控制返回的總頁數,要麼對超過特定閾值的頁數進行SQL 改寫。
    正例:先快速定位需要獲取的id 段,然後再關聯:

    SELECT a.* FROM 1 a, (select id from 1 where 條件LIMIT 100000,20 ) b where a.id=b.id

  2. 【推薦】SQL性能優化的目標:至少要達到 range級別,要求是 ref級別,如果可以是 consts最好。

    說明:

    1)consts單表中最多隻有一個匹配行(主鍵或者唯一索引),在優化階段即可讀取到數據。2)ref指的是使用普通的索引(normal index)
    3)range對索引進行範圍檢索。

    反例:explain表的結果,type=index,索引物理文件全掃描,速度非常慢,這個index 級別比較range 還低,與全表掃描是小巫見大巫。

  3. 【推薦】建組合索引的時候,區分度最高的在最左邊。
    正例:如果where a=?and b=? ,a列的幾乎接近於唯一值,那麼只需要單建 idx_a索引即可。說明:存在非等號和等號混合判斷條件時,在建索引時,請把等號條件的列前置。如:where a>?and b=?那麼即使 a的區分度更高,也必須把 b放在索引的最前列。

10.【參考】創建索引時避免有如下極端誤解:1)誤認爲一個查詢就需要建一個索引。2)誤認爲索引會消耗空間、嚴重拖慢更新和新增速度。3)誤認爲唯一索引一律需要在應用層通過先查後插方式解決。

——禁止用於商業用途,違者必究——

24 /34

阿里巴巴 Java開發手冊

() SQL規約

  1. 【強制】不要使用 count(列名)count(常量)來替代count(*),count(*)就是SQL92 定義的標準統計行數的語法,跟數據庫無關,跟 NULL和非 NULL無關。說明:count(*)會統計值爲NULL 的行,而count(列名)不會統計此列爲NULL 值的行。

  2. 【強制】count(distinct col)計算該列除 NULL之外的不重複數量。注意 count(distinctcol1,col2)如果其中一列全爲NULL,那麼即使另一列有不同的值,也返回爲0

  3. 【強制】當某一列的值全是 NULL時,count(col)的返回結果爲0,但sum(col)的返回結果爲NULL,因此使用sum()時需注意NPE 問題。正例:可以使用如下方式來避免sumNPE問題:SELECT IF(ISNULL(SUM(g)),0,SUM(g))FROM table;

  4. 【強制】使用 ISNULL()來判斷是否爲NULL 值。注意:NULL與任何值的直接比較都爲 NULL說明:

    1)NULL<>NULL的返回結果是NULL,而不是false2)NULL=NULL的返回結果是NULL,而不是true3)NULL<>1的返回結果是NULL,而不是true

  5. 【強制】 在代碼中寫分頁查詢邏輯時,若 count0應直接返回,避免執行後面的分頁語句。

  6. 【強制】不得使用外鍵與級聯,一切外鍵概念必須在應用層解決。說明:(概念解釋)學生表中的student_id是主鍵,那麼成績表中的 student_id則爲外鍵。如果更新學生表中的 student_id,同時觸發成績表中的student_id更新,則爲級聯更新。外鍵與級聯更新適用於單機低併發,不適合分佈式、高併發集羣;級聯更新是強阻塞,存在數據庫更新風暴的風險;外鍵影響數據庫的插入速度。

  7. 【強制】禁止使用存儲過程,存儲過程難以調試和擴展,更沒有移植性。

  8. 【強制】數據訂正時,刪除和修改記錄時,要先 select,避免出現誤刪除,確認無誤才能執

    行更新語句。

  9. 【推薦】in操作能避免則避免,若實在避免不了,需要仔細評估 in 後邊的集合元素數量,控

    制在 1000個之內。

10.【參考】如果有全球化需要,所有的字符存儲與表示,均以utf-8編碼,那麼字符計數方法注意:

說明:

SELECT LENGTH("輕鬆工作");返回爲12
SELECT CHARACTER_LENGTH("輕鬆工作");返回爲4如果要使用表情,那麼使用utfmb4來進行存儲,注意它與utf-8編碼的區別。

——禁止用於商業用途,違者必究——25 /34

阿里巴巴 Java開發手冊
11.【參考】TRUNCATE TABLEDELETE速度快,且使用的系統和事務日誌資源少,但TRUNCATE

無事務且不觸發trigger,有可能造成事故,故不建議在開發代碼中使用此語句。說明:TRUNCATE TABLE 在功能上與不帶WHERE子句的DELETE語句相同。

() ORM規約

  1. 【強制】在表查詢中,一律不要使用 *作爲查詢的字段列表,需要哪些字段必須明確寫明。

    說明:1)增加查詢分析器解析成本。2)增減字段容易與resultMap 配置不一致。

  2. 【強制】POJO類的 boolean屬性不能加 is,而數據庫字段必須加is_,要求在resultMap 中進行字段與屬性之間的映射。
    說明:參見定義POJO 類以及數據庫字段定義規定,在sql.xml增加映射,是必須的。

  3. 【強制】不要用 resultClass當返回參數,即使所有類屬性名與數據庫字段一一對應,也需要定義;反過來,每一個表也必然有一個與之對應。
    說明:配置映射關係,使字段與DO 類解耦,方便維護。

  4. 【強制】xml配置中參數注意使用:#{},#param#不要使用${}此種方式容易出現 SQL注入。

5.【強制】iBATIS自帶的queryForList(String statementName,int start,int size)不推薦使用。

說明:其實現方式是在數據庫取到statementName對應的SQL語句的所有記錄,再通過subListstart,size的子集合,線上因爲這個原因曾經出現過OOM

正例:sqlmap.xml中引入#start#, #size#

Map<String, Object> map = new HashMap<String, Object>();map.put("start", start);
map.put("size", size);

  1. 【強制】不允許直接拿 HashMapHashtable作爲查詢結果集的輸出。

  2. 【強制】更新數據表記錄時,必須同時更新記錄對應的 gmt_modified字段值爲當前時間。

  3. 【推薦】不要寫一個大而全的數據更新接口,傳入爲 POJO類,不管是不是自己的目標更新字段,都進行 update table set c1=value1,c2=value2,c3=value3;這是不對的。執行 SQL時,儘量不要更新無改動的字段,一是易出錯;二是效率低;三是binlog 增加存儲。

  4. 【參考】@Transactional事務不要濫用。事務會影響數據庫的 QPS,另外使用事務的地方需要考慮各方面的回滾方案,包括緩存回滾、搜索引擎回滾、消息補償、統計修正等。

10.【參考】<isEqual>中的compareValue是與屬性值對比的常量,一般是數字,表示相等時帶上此條件;<isNotEmpty>表示不爲空且不爲null時執行;<isNotNull>表示不爲null值時執行。

——禁止用於商業用途,違者必究——26 /34

阿里巴巴 Java開發手冊

四、工程規約

()應用分層
1.【推薦】圖中默認上層依賴於下層,箭頭關係表示可直接依賴,如:開放接口層可以依賴於

Web層,也可以直接依賴於Service層,依此類推:

  •   開放接口層:可直接封裝Service接口暴露成RPC接口;通過Web封裝成http接口;網關控制層等。

  •   終端顯示層:各個端的模板渲染並執行顯示層。當前主要是velocity渲染,JS渲染,JSP渲染,移動端展示層等。

  •   Web層:主要是對訪問控制進行轉發,各類基本參數校驗,或者不復用的業務簡單處理等。

  •   Service層:相對具體的業務邏輯服務層。

  •   Manager層:通用業務處理層,它有如下特徵:
    1)對第三方平臺封裝的層,預處理返回結果及轉化異常信息;
    2)Service層通用能力的下沉,如緩存方案、中間件通用處理;3)DAO層交互,對DAO的業務通用能力的封裝。

  •   DAO層:數據訪問層,與底層MySQLOracleHbase進行數據交互。

  •   外部接口或第三方平臺:包括其它部門RPC開放接口,基礎平臺,其它公司的HTTP接口。

    2.【參考】(分層異常處理規約)DAO層,產生的異常類型有很多,無法用細粒度異常進行catch,使用catch(Exception e)方式,並throw new DAOException(e),不需要打印日誌,因爲日誌在Manager/Service層一定需要捕獲並打到日誌文件中去,如果同臺服務器再打日誌,浪費性能和存儲。在Service層出現異常時,必須記錄日誌信息到磁盤,儘可能帶上參數信息,相當於保護案發現場。如果Manager層與Service同機部署,日誌方式與DAO層處理一致,如果是單獨部署,則採用與Service一致的處理方式。Web層絕不應該繼續往上拋異常,

——禁止用於商業用途,違者必究——27 /34

阿里巴巴 Java開發手冊因爲已經處於頂層,無繼續處理異常的方式,如果意識到這個異常將導致頁面無法正常渲染,

那麼就應該直接跳轉到友好錯誤頁面,儘量加上友好的錯誤提示信息。開放接口層要將異常處理成錯誤碼和錯誤信息方式返回。

3.【參考】分層領域模型規約:

  •   DO(Data Object):與數據庫表結構一一對應,通過DAO層向上傳輸數據源對象。

  •   DTO(Data Transfer Object):數據傳輸對象,ServiceManager向外傳輸的對象。

  •   BO(Business Object):業務對象。可以由Service層輸出的封裝業務邏輯的對象。

  •   QUERY:數據查詢對象,各層接收上層的查詢請求。注:超過2個參數的查詢封裝,禁止使用Map類來傳輸。

  •   VO(View Object):顯示層對象,通常是Web向模板渲染引擎層傳輸的對象。()二方庫規約

  1. 【強制】定義 GAV遵從以下規則:
    1)GroupID格式:com.{公司/BU}.業務線.[子業務線],最多4級。

    說明:{公司/BU}例如:alibaba/taobao/tmall/aliexpressBU一級;子業務線可選。

    正例:com.taobao.jstormcom.alibaba.dubbo.register
    2)ArtifactID格式:產品線名-模塊名。語義不重複不遺漏,先到倉庫中心去查證一下。

    正例:dubbo-client/ fastjson-api/ jstorm-tool3)Version:詳細規定參考下方。

  2. 【強制】二方庫版本號命名方式:主版本號.次版本號.修訂號
    1)主版本號:當做了不兼容的API修改,或者增加了能改變產品方向的新功能。2)次版本號:當做了向下兼容的功能性新增(新增類、接口等)
    3)修訂號:修復 bug,沒有修改方法簽名的功能加強,保持API 兼容性。

    說明:起始版本號必須爲:1.0.0,而不是0.0.1

  3. 【強制】線上應用不要依賴 SNAPSHOT版本(安全包除外);正式發佈的類庫必須使用RELEASE版本號升級+1的方式,且版本號不允許覆蓋升級,必須去中央倉庫進行查證。
    說明:不依賴SNAPSHOT 版本是保證應用發佈的冪等性。另外,也可以加快編譯時的打包構建。

  4. 【強制】二方庫的新增或升級,保持除功能點之外的其它 jar 包仲裁結果不變。如果有改變,必須明確評估和驗證,建議進行dependency:resolve前後信息比對,如果仲裁結果完全不一致,那麼通過 dependency:tree命令,找出差異點,進行<excludes>排除jar 包。

  5. 【強制】二方庫裏可以定義枚舉類型,參數可以使用枚舉類型,但是接口返回值不允許使用枚舉類型或者包含枚舉類型的POJO 對象。

——禁止用於商業用途,違者必究——28 /34

阿里巴巴 Java開發手冊
6.【強制】依賴於一個二方庫羣時,必須定義一個統一版本變量,避免版本號不一致。

說明:依賴springframework-core,-context,-beans,它們都是同一個版本,可以定義一個變量來保存版本:${spring.version},定義依賴的時候,引用該版本。

7.【強制】禁止在子項目的pom依賴中出現相同的GroupId,相同的ArtifactId,但是不同的Version

說明:在本地調試時會使用各子項目指定的版本號,但是合併成一個war,只能有一個版本號出現在最後的lib目錄中。曾經出現過線下調試是正確的,發佈到線上出故障的先例。

8.【推薦】所有pom文件中的依賴聲明放在<dependencies>語句塊中,所有版本仲裁放在<dependencyManagement>語句塊中。說明:<dependencyManagement>裏只是聲明版本,並不實現引入,因此子項目需要顯式的聲明依賴,versionscope都讀取自父pom。而<dependencies>所有聲明在主pom<dependencies>裏的依賴都會自動引入,並默認被所有的子項目繼承。

9.【推薦】二方庫儘量不要有配置項,最低限度不要再增加配置項。

10.【參考】爲避免應用二方庫的依賴衝突問題,二方庫發佈者應當遵循以下原則:1)精簡可控原則。移除一切不必要的API和依賴,只包含Service API、必要的領域模型對象、Utils類、常量、枚舉等。如果依賴其它二方庫,儘量是provided引入,讓二方庫使用者去依賴具體版本號;log具體實現,只依賴日誌框架。2)穩定可追溯原則。每個版本的變化應該被記錄,二方庫由誰維護,源碼在哪裏,都需要能方便查到。除非用戶主動升級版本,否則公共二方庫的行爲不應該發生變化。

——禁止用於商業用途,違者必究——29 /34

阿里巴巴 Java開發手冊

()服務器規約

1.【推薦】高併發服務器建議調小TCP協議的time_wait超時時間。
說明:操作系統默認240秒後,纔會關閉處於time_wait狀態的連接,在高併發訪問下,服務器端會因爲處於time_wait的連接數太多,可能無法建立新的連接,所以需要在服務器上調小此等待值。
正例:linux服務器上請通過變更/etc/sysctl.conf文件去修改該缺省值():

net.ipv4.tcp_fin_timeout= 30

2.【推薦】調大服務器所支持的最大文件句柄數(File Descriptor,簡寫爲fd)說明:主流操作系統的設計是將TCP/UDP連接採用與文件一樣的方式去管理,即一個連接對應於一個fd。主流的linux服務器默認所支持最大fd數量爲1024,當併發連接數很大時很容易因爲fd不足而出現open too many files錯誤,導致新的連接無法建立。 建議將linux服務器所支持的最大句柄數調高數倍(與服務器的內存數量相關)

  1. 【推薦】給 JVM設置-XX:+HeapDumpOnOutOfMemoryError參數,讓 JVM碰到 OOM場景時輸出dump信息。

    說明:OOM的發生是有概率的,甚至有規律地相隔數月纔出現一例,出現時的現場信息對查錯非常有價值。

  2. 【參考】服務器內部重定向使用 forward;外部重定向地址使用URL 拼裝工具類來生成,否則會帶來URL 維護不一致的問題和潛在的安全風險。

——禁止用於商業用途,違者必究——30 /34

阿里巴巴 Java開發手冊

五、安全規約

  1. 【強制】隸屬於用戶個人的頁面或者功能必須進行權限控制校驗。說明:防止沒有做水平權限校驗就可隨意訪問、操作別人的數據,比如查看、修改別人的訂單。

  2. 【強制】用戶敏感數據禁止直接展示,必須對展示數據脫敏。說明:查看個人手機號碼會顯示成:158****9119,隱藏中間4 位,防止隱私泄露。

  3. 【強制】用戶輸入的 SQL參數嚴格使用參數綁定或者 METADATA字段值限定,防止 SQL注入,禁止字符串拼接 SQL訪問數據庫。

  4. 【強制】用戶請求傳入的任何參數必須做有效性驗證。說明:忽略參數校驗可能導致:

    page size過大導致內存溢出

    惡意order by 導致數據庫慢查詢

    任意重定向

    SQL注入

    反序列化注入

    正則輸入源串拒絕服務ReDoS

    說明:Java代碼用正則來驗證客戶端的輸入,有些正則寫法驗證普通用戶輸入沒有問題,但是如果攻擊人員使用的是特殊構造的字符串來驗證,有可能導致死循環的效果。

  5. 【強制】禁止向 HTML頁面輸出未經安全過濾或未正確轉義的用戶數據。

  6. 【強制】表單、AJAX提交必須執行 CSRF安全過濾。
    說明:CSRF(Cross-site request forgery)跨站請求僞造是一類常見編程漏洞。對於存在CSRF漏洞的應用/網站,攻擊者可以事先構造好URL,只要受害者用戶一訪問,後臺便在用戶不知情情況下對數據庫中用戶參數進行相應修改。

  7. 【強制】在使用平臺資源,譬如短信、郵件、電話、下單、支付,必須實現正確的防重放限制,如數量限制、疲勞度控制、驗證碼校驗,避免被濫刷、資損。說明:如註冊時發送驗證碼到手機,如果沒有限制次數和頻率,那麼可以利用此功能騷擾到其它用戶,並造成短信平臺資源浪費。

  8. 【推薦】發貼、評論、發送即時消息等用戶生成內容的場景必須實現防刷、文本內容違禁詞過濾等風控策略。 





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章