天天看點

點評阿裡JAVA手冊之程式設計規約(命名風格、常量定義、代碼風格、控制語句、注釋規約)

 下載下傳原版阿裡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"); 

反例: 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 标記某代碼是錯誤的,而且不能工作,需要及時糾正的情況