下載原版阿里JAVA開發手冊 【阿里巴巴Java開發手冊v1.2.0】
本文主要是對照阿里開發手冊,注釋自己在工作中運用情況。
本文難度系數為一星(★)
第一篇 點評阿里JAVA手冊之編程規約(命名風格、常量定義、代碼風格、控制語句、注釋規約)
第二篇 點評阿里JAVA手冊之編程規約(OOP 規約 、集合處理 、並發處理 、其他)
第三篇 點評阿里JAVA手冊之異常日志(異常處理 日志規約 )
第四篇 點評阿里JAVA手冊之MySQL數據庫 (建表規約、索引規約、SQL語句、ORM映射)
碼出高效、碼出質量。
代碼的字里行間流淌的是軟件生命中的血液,質量的提升是盡可能少踩坑,杜絕踩重復的坑,切實提升質量意識。另外,現代軟件架構都需要協同開發完成,高效協作即降低協同成本,提升溝通效率,所謂無規矩不成方圓,無規范不能協作。眾所周知,制訂交通法規表面上是要限制行車權,實際上是保障公眾的人身安全。試想如果沒有限速,沒有紅綠燈,誰還敢上路行駛。對軟件來說,適當的規范和標准絕不是消滅代碼內容的創造性、優雅性,而是限制過度個性化,以一種普遍認可的統一方式一起做事,提升協作效率。
一、編程規約
(一) 命名風格
1. 【強制】 代碼中的命名均不能以下划線或美元符號開始,也不能以下划線或美元符號結束。
反例: _name / __name / $Object / name_ / name$ / 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
框架在反向解析的時候,“以為”對應的屬性名稱是 deleted,導致屬性獲取不到,進而拋出異 常。
【點評】 存疑,如果數據庫設計表字段,用is開頭的數據庫字段。 進入orm時一般自動生成實體類代碼,很可能存在。自己在設計數據庫時,多用flag結尾作為tinyint去表示布爾型,不存在此情況。
9.【強制】包名統一使用小寫,點分隔符之間有且僅有一個自然語義的英語單詞。包名統一使用 單數形式,但是類名如果有復數含義,類名可以使用復數形式。 正例: 應用工具類包名為 com.alibaba.open.util、類名為 MessageUtils(此規則參考 spring 的框架結構)
【點評】規則好,以后在項目中遵循。
目前代碼中有com.wxhealth.util.wechat。
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 方法,是對所有實現類都有價值的默認實現。
【點評】規則好,很少注意到這樣使用。
在interface里面的變量都是public static final 的。所以你可以這樣寫:
public static final int i=10;
或則
int i=10;(可以省略掉一部分) 注意在聲明的時候要給變量賦予初值。
首先你要弄清接口的含義.接口就是提供一種統一的’協議’,而接口中的屬性也屬於’協議’中的成員.它們是公共的,靜態的,最終的常量.相當於全局常量.
抽象類是不’完全’的類,相當於是接口和具體類的一個中間層.即滿足接口的抽象,也滿足具體的實現.如果接口可以定義變量,但是接口中的方法又都是抽象的,在接口中無法通過行為來修改屬性。有的人會說了,沒有關系,可以通過實現接口的對象的行為來修改接口中的屬性。這當然沒有問題,但是考慮這樣的情況。如果接口A中有一個public訪問權限的靜態變量a。按照Java的語義,我們可以不通過實現接口的對象來訪問變量a,通過A.a = xxx;就可以改變接口中的變量a的值了。正如抽象類中是可以這樣做的,那么實現接口A的所有對象也都會自動擁有這一改變后的a的值了,也就是說一個地方改變了a,所有這些對象中a的值也都跟着變了。這和抽象類有什么區別呢,怎么體現接口更高的抽象級別呢,怎么體現接口提供的統一的協議呢,那還要接口這種抽象來做什么呢?所以接口中不能出現變量,如果有變量,就和接口提供的統一的抽象這種思想是抵觸的。所以接口中的屬性必然是常量,只能讀不能改,這樣才能為實現接口的對象提供一個統一的屬性。通俗的講,你認為是要變化的東西,就放在你自己的實現中,不能放在接口中去,接口只是對一類事物的屬性和行為更高層次的抽象。對修改關閉,對擴展(不同的實現implements)開放,接口是對開閉原則的一種體現。轉自:http://blog.csdn.net/ameyume/article/details/6189749
13. 接口和實現類的命名有兩套規則:
1)【強制】對於 Service 和 DAO 類,基於 SOA 的理念,暴露出來的服務一定是接口,內部的實現類用 Impl 的后綴與接口區別。
正例:CacheServiceImpl 實現 CacheService 接口。
2)【推薦】 如果是形容能力的接口名稱,取對應的形容詞做接口名(通常是–able 的形式)。
正例:AbstractTranslator 實現 Translatable。
【點評】規則好,使用中自己多這樣使用:IApiProcessor
14.【參考】枚舉類名建議帶上 Enum 后綴,枚舉成員名稱需要全大寫,單詞間用下划線隔開。 說明:枚舉其實就是特殊的常量類,且構造方法被默認強制是私有。
正例:枚舉名字:DealStatusEnum,成員名稱:SUCCESS / UNKOWN_REASON。
【點評】規則好,嚴格遵循。
15.【參考】各層命名規約:
A) Service/DAO 層方法命名規約
1) 獲取單個對象的方法用 get 做前綴。
【點評】規則好,嚴格遵循。
2) 獲取多個對象的方法用 list 做前綴。
【點評】規則存疑 ,自己用get開頭,實體類名加List 。getOrderListByName()
3) 獲取統計值的方法用 count 做前綴。
【點評】規則存疑 ,自己用get開頭,實體類名加Count 。getOrderCountByName()
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);
【點評】規則好,在代碼中盡量遵循。但是,我們在api接口中有:
Map<String, Object> response = new HashMap<String, Object>();
response.put("code", 0);
response.put("orderflag", orderflag);
將response轉化為json格式,通過rest api發布。
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 定義。
【點評】規則存疑,直接在類中定義跨應用共享常量。如Integer.MAX_VALUE 和Integer.MIN_VALUE。
public final class Integer extends Number implements Comparable<Integer> {
/**
* A constant holding the minimum value an {@code int} can
* have, -2<sup>31</sup>.
*/
public static final int MIN_VALUE = 0x80000000;
/**
* A constant holding the maximum value an {@code int} can
* have, 2<sup>31</sup>-1.
*/
public static final int MAX_VALUE = 0x7fffffff;
。。。。。。
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 等保留字與括號之間都必須加空格。
【點評】規則好,未遵循。 在Eclipse下面,用格式化代碼ctrl+shift+f 是這樣的。
4. 【強制】任何二目、三目運算符的左右兩邊都需要加一個空格。
說明:運算符包括賦值運算符=、邏輯運算符&&、加減乘除符號等。
【點評】規則好,未遵循。 在Eclipse下面,用格式化代碼ctrl+shift+f 是這樣的。
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"); // 在右大括號后直接結束,則必須換行
}
}
【點評】規則好,已經遵循。
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. 【推薦】方法體內的執行語句組、變量的定義語句組、不同的業務邏輯之間或者不同的語義之間插入一個空行。相同業務邏輯和語義之間不需要插入空行。
說明:沒有必要插入多個空行進行隔開
【點評】規則好,已經遵循。
(四) 控制語句
1. 【強制】在一個 switch 塊內,每個 case 要么通過 break/return 等來終止,要么注釋說明程 序將繼續執行到哪一個 case 為止;在一個 switch 塊內,都必須包含一個 default 語句並且 放在最后,即使它什么代碼也沒有。
【點評】規則好,嚴格遵循。
2. 【強制】在 if/else/for/while/do 語句中必須使用大括號。即使只有一行代碼,避免使用 單行的形式:if (condition) statements;
【點評】規則好,嚴格遵循。
3. 【推薦】表達異常的分支時,少用 if-else 方式,這種方式可以改寫成:
if (condition) { ... return obj; }
// 接着寫 else 的業務邏輯代碼; 說明:如果非得使用 if()...else if()...else...方式表達邏輯, 【強制】避免后續代碼維護困難,請勿超過 3 層。
正例:邏輯上超過 3 層的 if-else 代碼可以使用衛語句,或者狀態模式來實現。語句示例 如下:
public void today() {
if (isBusy()) { System.out.println(“change time.”); return; }
if (isFree()) { System.out.println(“go to travel.”); return; }
System.out.println(“stay at home to learn Alibaba Java Coding Guideline.”); return; }
【點評】規則好,嚴格遵循。
4. 【推薦】除常用方法(如 getXxx/isXxx)等外,不要在條件判斷中執行其它復雜的語句,將復雜邏輯判斷的結果賦值給一個有意義的布爾變量名,以提高可讀性。
說明:很多 if 語句內的邏輯相當復雜,閱讀者需要分析條件表達式的最終結果,才能明確什么樣的條件執行什么樣的語句,那么,如果閱讀者分析邏輯表達式錯誤呢? 正例: //偽代碼如下 final boolean existed = (file.open(fileName, "w") != null) && (...) || (...);
if (existed) { ... }
反例: if ((file.open(fileName, "w") != null) && (...) || (...)) { ... }
【點評】規則好,嚴格遵循。
5. 【推薦】循環體中的語句要考量性能,以下操作盡量移至循環體外處理,如定義對象、變量、 獲取數據庫連接,進行不必要的 try-catch 操作(這個 try-catch 是否可以移至循環體外)。
【點評】規則好,嚴格遵循。
6. 【推薦】接口入參保護,這種場景常見的是用於做批量操作的接口。
7. 【參考】下列情形,需要進行參數校驗:
1) 調用頻次低的方法。
2) 執行時間開銷很大的方法。此情形中,參數校驗時間幾乎可以忽略不計,但如果因為參數錯誤導致中間執行回退,或者錯誤,那得不償失。
3) 需要極高穩定性和可用性的方法。
4) 對外提供的開放接口,不管是 RPC/API/HTTP 接口。
5) 敏感權限入口。
【點評】規則好,嚴格遵循。
8. 【參考】下列情形,不需要進行參數校驗:
1) 極有可能被循環調用的方法。但在方法說明里必須注明外部參數檢查要求。
2) 底層調用頻度比較高的方法。畢竟是像純凈水過濾的最后一道,參數錯誤不太可能到底 層才會暴露問題。一般 DAO 層與 Service 層都在同一個應用中,部署在同一台服務器中,所 以 DAO 的參數校驗,可以省略。
3) 被聲明成 private 只會被自己代碼所調用的方法,如果能夠確定調用方法的代碼傳入參 數已經做過檢查或者肯定不會有問題,此時可以不校驗參數
【點評】規則好,嚴格遵循。
(五) 注釋規約
1. 【強制】類、類屬性、類方法的注釋必須使用 Javadoc 規范,使用/**內容*/格式,不得使用 //xxx 方式。 說明:在 IDE 編輯窗口中,Javadoc 方式會提示相關注釋,生成 Javadoc 可以正確輸出相應注 釋;在 IDE 中,工程調用方法時,不進入方法即可懸浮提示方法、參數、返回值的意義,提高 閱讀效率。
【點評】規則好,嚴格遵循。自己目前未嚴格遵守。
Javadoc 規范參考:http://www.2cto.com/kf/201607/521803.html
http://www.cnblogs.com/bluestorm/archive/2012/10/04/2711329.html
2. 【強制】所有的抽象方法(包括接口中的方法)必須要用 Javadoc 注釋、除了返回值、參數、 異常說明外,還必須指出該方法做什么事情,實現什么功能。 說明:對子類的實現要求,或者調用注意事項,請一並說明。
【點評】規則好,嚴格遵循。
3. 【強制】所有的類都必須添加創建者和創建日期。
【點評】規則好,嚴格遵循。自己目前未嚴格遵守。
4. 【強制】方法內部單行注釋,在被注釋語句上方另起一行,使用//注釋。方法內部多行注釋 使用/* */注釋,注意與代碼對齊。
【點評】規則好,嚴格遵循。自己目前未嚴格遵守。
5. 【強制】所有的枚舉類型字段必須要有注釋,說明每個數據項的用途。
【點評】規則好,嚴格遵循。自己目前未嚴格遵守。
6. 【推薦】與其“半吊子”英文來注釋,不如用中文注釋把問題說清楚。專有名詞與關鍵字保持 英文原文即可。 反例:“TCP 連接超時”解釋成“傳輸控制協議連接超時”,理解反而費腦筋。
【點評】規則好,嚴格遵循。
7. 【推薦】代碼修改的同時,注釋也要進行相應的修改,尤其是參數、返回值、異常、核心邏輯 等的修改。 說明:代碼與注釋更新不同步,就像路網與導航軟件更新不同步一樣,如果導航軟件嚴重滯后, 就失去了導航的意義。
【點評】規則好,嚴格遵循。
8. 【參考】合理處理注釋掉的代碼。在上方詳細說明,而不是簡單的注釋掉。如果無用,則刪除。 說明:代碼被注釋掉有兩種可能性:1)后續會恢復此段代碼邏輯。2)永久不用。前者如果沒有備注信息,難以知曉注釋動機。后者建議直接刪掉(代碼倉庫保存了歷史代碼)。
【點評】規則好,嚴格遵循。
9. 【參考】對於注釋的要求:第一、能夠准確反應設計思想和代碼邏輯;第二、能夠描述業務含 義,使別的程序員能夠迅速了解到代碼背后的信息。完全沒有注釋的大段代碼對於閱讀者形同
天書,注釋是給自己看的,即使隔很長時間,也能清晰理解當時的思路;注釋也是給繼任者看的,使其能夠快速接替自己的工作。
【點評】規則好,嚴格遵循。
10. 【參考】好的命名、代碼結構是自解釋的,注釋力求精簡准確、表達到位。避免出現注釋的 一個極端:過多過濫的注釋,代碼的邏輯一旦修改,修改注釋是相當大的負擔。 反例: // put elephant into fridge put(elephant, fridge); 方法名 put,加上兩個有意義的變量名 elephant 和 fridge,已經說明了這是在干什么,語 義清晰的代碼不需要額外的注釋。
【點評】規則好,嚴格遵循。
11. 【參考】特殊注釋標記,請注明標記人與標記時間。注意及時處理這些標記,通過標記掃描, 經常清理此類標記。線上故障有時候就是來源於這些標記處的代碼。
1) 待辦事宜(TODO):( 標記人,標記時間,[預計處理時間]) 表示需要實現,但目前還未實現的功能。這實際上是一個 Javadoc 的標簽,目前的 Javadoc 還沒有實現,但已經被廣泛使用。只能應用於類,接口和方法(因為它是一個 Javadoc 標簽)。
2) 錯誤,不能工作(FIXME):(標記人,標記時間,[預計處理時間]) 在注釋中用 FIXME 標記某代碼是錯誤的,而且不能工作,需要及時糾正的情況
【點評】規則好,嚴格遵循。