阿里java編程規範

前言 
《阿里巴巴Java開發手冊》是阿里巴巴集團技術團隊的集體經驗總結,經歷了多次大規模一線實戰的檢驗及不斷的完善,反饋給廣大開發者。現代軟件行業的高速發展對開發者的綜合素質要求越來越高,因爲不僅是編程知識點,其它維度的知識點也會影響到軟件的最終交付質量。比如:數據庫的表結構和索引設計缺陷可能帶來軟件上的架構缺陷或性能風險;工程結構混亂導致後續維護艱難;沒有鑑權的漏洞代碼易被黑客攻擊等等。所以本手冊以Java開發者爲中心視角,劃分爲編程規約、異常日誌、MySQL數據庫、工程結構、安全規約五大塊,再根據內容特徵,細分成若干二級子目錄。根據約束力強弱及故障敏感性,規約依次分爲強制、推薦、參考三大類。對於規約條目的延伸信息中,“說明”對內容做了引申和解釋;“正例”提倡什麼樣的編碼和實現方式;“反例”說明需要提防的雷區,以及真實的錯誤案例。 
本手冊的願景是碼出高效、碼出質量。代碼的字裏行間流淌的是軟件生命中的血液,質量的提升是儘可能少踩坑,杜絕踩重複的坑,切實提升質量意識。另外,現代軟件架構都需要協同開發完成,高效協作即降低協同成本,提升溝通效率,所謂無規矩不成方圓,無規範不能協作。衆所周知,制訂交通法規表面上是要限制行車權,實際上是保障公衆的人身安全。試想如果沒有限速,沒有紅綠燈,誰還敢上路行駛。對軟件來說,適當的規範和標準絕不是消滅代碼內容的創造性、優雅性,而是限制過度個性化,以一種普遍認可的統一方式一起做事,提升協作效率。 
《阿里巴巴Java開發手冊》,開放包容地認真聽取社區、博客、論壇的反饋,及時修正,保持與時俱進。請關注手冊末頁的“阿里技術”和“雲棲社區”公衆號獲取最新版本。 
目錄 前言 一、編程規約
(一) 命名風格 
(二) 常量定義
(三) 代碼格式
(四) OOP規約
(五) 集合處理
(六) 併發處理
(七) 控制語句
(八) 註釋規約
(九) 其它
(一) 異常處理

(二) 日誌規約

三、MySQL數據庫

(一) 建表規約
(二) 索引規約
(三) SQL語句

(四) ORM映射

四、工程結構 

(一) 應用分層
(二) 二方庫依賴

(三) 服務器

 五、安全規約 

 附 1:版本歷史 :版本歷史 :版本歷史

 附 2:本手冊專有名詞 :本手冊專有名詞 :本手冊專有名詞 :本手冊專有名詞 

(注 :瀏覽 時請使用 PDF左側導航欄) 
阿里巴巴 Java 開發手冊 
 
一、編程規約 
(一) 命名風格 
1. 【強制】 代碼中的命名均不能以下劃線或美元符號開始,也不能以下劃線或美元符號結束。 反例: name _name _name / __name __name __name / ObjectObjectObject ObjectObjectObject / name name_ name_ / namenamename name/Object/ObjectObjectObjectObject Object$ 
2. 【強制】 代碼中的命名嚴禁使用拼音與英文混合的方式,更不允許直接使用中文的方式。 說明:正確的英文拼寫和語法可以讓閱讀者易於理解,避免歧義。注意,即使純拼音命名方式也要避免採用。 
正例: alibaba / taobao / youku / hangzhou 等國際通用的名稱,可視同英文。 反例: DaZhePromotion [打折] / getPingfenByName() [評分] / int 某變量 = 3 
3. 【強制】類名使用UpperCamelCase風格,必須遵從駝峯形式,但以下情形例外:DO / BO / DTO / VO / AO 正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion 反例:macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion 
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 isDeleted;的屬性,它的方法也是isDeleted(),RPC 
阿里巴巴 Java 開發手冊 
——禁止用於商業用途,違者必究—— 2 / 33 
框架在反向解析的時候,“以爲”對應的屬性名稱是deleted,導致屬性獲取不到,進而拋出異常。 
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)【強制】對於Service和DAO類,基於SOA的理念,暴露出來的服務一定是接口,內部的實現類用Impl的後綴與接口區別。 正例:CacheServiceImpl實現CacheService接口。 2) 【推薦】 如果是形容能力的接口名稱,取對應的形容詞做接口名(通常是–able的形式)。 正例:AbstractTranslator實現 Translatable。 
14. 【參考】枚舉類名建議帶上Enum後綴,枚舉成員名稱需要全大寫,單詞間用下劃線隔開。 說明:枚舉其實就是特殊的常量類,且構造方法被默認強制是私有。 正例:枚舉名字:DealStatusEnum,成員名稱:SUCCESS / UNKOWN_REASON。 
15. 【參考】各層命名規約: A) Service/DAO層方法命名規約 1) 獲取單個對象的方法用get做前綴。 
阿里巴巴 Java 開發手冊 
——禁止用於商業用途,違者必究—— 3 / 33 
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下。 說明:大而全的常量類,非得使用查找功能才能定位到修改的常量,不利於理解和維護。 
4. 【推薦】常量的複用層次有五層:跨應用共享常量、應用內共享常量、子工程內共享常量、包內共享常量、類內共享常量。 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) 子工程內部共享常量:即在當前子工程的constant目錄下。 4) 包內共享常量:即在當前包下單獨的constant目錄下。 5) 類內共享常量:直接在類內部private static final定義。 
阿里巴巴 Java 開發手冊 
——禁止用於商業用途,違者必究—— 4 / 33 
5. 【推薦】如果變量值僅在一個範圍內變化,且帶有名稱之外的延伸屬性,定義爲枚舉類。下面正例中的數字就是延伸信息,表示星期幾。 正例:public Enum { MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4), FRIDAY(5), SATURDAY(6), SUNDAY(7);} 
(三) 代碼格式 
1. 【強制】大括號的使用約定。如果是大括號內爲空,則簡潔地寫成{}即可,不需要換行;如果是非空代碼塊則: 1) 左大括號前不換行。 2) 左大括號後換行。 3) 右大括號前換行。 4) 右大括號後還有else等代碼則不換行;表示終止的右大括號後必須換行。 
2. 【強制】 左小括號和字符之間不出現空格;同樣,右小括號和字符之間也不出現空格。詳見第5條下方正例提示。 反例:if (空格a == b空格) 
3. 【強制】if/for/while/switch/do等保留字與括號之間都必須加空格。 
4. 【強制】任何二目、三目運算符的左右兩邊都需要加一個空格。 說明:運算符包括賦值運算符=、邏輯運算符&&、加減乘除符號等。 
5. 【強制】縮進採用4個空格,禁止使用tab字符。 
說明: 如果使用 tab 縮進,必須設置 縮進,必須設置 縮進,必須設置 縮進,必須設置 縮進,必須設置 縮進,必須設置 1個 tab 爲 4個空格。 IDEA 設置 tab 爲 4個空格時, 請勿勾選 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,不用換行 
} else { 
System.out.println(“ok”); 
阿里巴巴 Java 開發手冊 
——禁止用於商業用途,違者必究—— 5 / 33 
// 在右大括號後直接結束,則必須換行 

} 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. 【強制】IDE的text file encoding設置爲UTF-8; IDE中文件的換行符使用Unix格式,不要使用windows格式。 
9. 【推薦】沒有必要增加若干空格來使某一行的字符與上一行對應位置的字符對齊。 正例: 
int a = 3; 
long b = 4L; 
float c = 5F; 
StringBuffer sb = new StringBuffer(); 
說明:增加sb這個變量,如果需要對齊,則給a、b、c都要增加幾個空格,在變量比較多的情況下,是一種累贅的事情。 
10. 【推薦】方法體內的執行語句組、變量的定義語句組、不同的業務邏輯之間或者不同的語義之間插入一個空行。相同業務邏輯和語義之間不需要插入空行。 說明:沒有必要插入多個空行進行隔開。 
阿里巴巴 Java 開發手冊 
——禁止用於商業用途,違者必究—— 6 / 33 
(四) 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方法比較。 說明:對於Integer var = ? 在-128至127範圍內的賦值,Integer對象是在IntegerCache.cache產生,會複用已有對象,這個區間內的Integer值可以直接使用==進行判斷,但是這個區間之外的所有數據,都會在堆上產生,並不會複用已有對象,這是一個大坑,推薦使用equals方法進行判斷。 
8. 關於基本數據類型與包裝數據類型的使用標準如下: 1) 【強制】所有的POJO類屬性必須使用包裝數據類型。 2) 【強制】RPC方法的返回值和參數必須使用包裝數據類型。 3) 【推薦】所有的局部變量使用基本數據類型。 說明:POJO類屬性沒有初值是提醒使用者在需要使用時,必須自己顯式地進行賦值,任何NPE問題,或者入庫檢查,都由使用者來保證。 正例:數據庫的查詢結果可能是null,因爲自動拆箱,用基本數據類型接收有NPE風險。 
阿里巴巴 Java 開發手冊 
——禁止用於商業用途,違者必究—— 7 / 33 
反例:比如顯示成交總額漲跌情況,即正負x%,x爲基本數據類型,調用的RPC服務,調用不成功時,返回的是默認值,頁面顯示:0%,這是不合理的,應該顯示成中劃線-。所以包裝數據類型的null值,能夠表示額外的信息,如:遠程調用失敗,異常退出。 
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()方法打印其屬性值,便於排查問題。 
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 { 
阿里巴巴 Java 開發手冊 
——禁止用於商業用途,違者必究—— 8 / 33 
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方法實現屬性對象的拷貝。 
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的成員變量,刪除一下,不得手心冒點汗嗎?變量像自己的小孩,儘量在自己的視線內,變量作用域太大,如果無限制的到處跑,那麼你會擔心的。 
阿里巴巴 Java 開發手冊 
——禁止用於商業用途,違者必究—— 9 / 33 
(五) 集合處理 
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 異常。 
4. 【強制】使用集合轉數組的方法,必須使用集合的toArray(T[] array),傳入的是類型完全一樣的數組,大小就是list.size()。 
說明:使用toArray帶參方法,入參分配的數組空間不夠大時,toArray方法內部將重新分配內存空間,並返回新數組地址;如果數組元素大於實際所需,下標爲[ list.size() ]的數組元素將被置爲null,其它數組元素保持原值,因此最好將方法入參數組大小定義與集合元素個數一致。 正例: 
List list = new ArrayList(2); 
list.add(“guan”); 
list.add(“bao”); 
String[] array = new String[list.size()]; 
array = list.toArray(array); 
反例:直接使用toArray無參方法存在問題,此方法返回值只能是Object[]類,若強轉其它類型數組將出現ClassCastException錯誤。 
5. 【強制】使用工具類Arrays.asList()把數組轉換成集合時,不能使用其修改集合相關的方法,它的add/remove/clear方法會拋出UnsupportedOperationException異常。 說明:asList的返回對象是一個Arrays內部類,並沒有實現集合的修改方法。Arrays.asList體現的是適配器模式,只是轉換接口,後臺的數據仍是數組。 String[] str = new String[] { “a”, “b” }; List list = Arrays.asList(str); 
阿里巴巴 Java 開發手冊 
——禁止用於商業用途,違者必究—— 10 / 33 
第一種情況:list.add(“c”); 運行時異常。 第二種情況:str[0] = “gujin”; 那麼list.get(0)也會隨之修改。 
6. 【強制】泛型通配符
發佈了26 篇原創文章 · 獲贊 80 · 訪問量 31萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章