JavaSE筆記2:編程規範

2.Java命名規範

Java領域一般使用駝峯命令法。變量和方法都是第一個字母小寫,之後每個單詞的第一個大寫。google和阿里都發布過一些編程規範相關都手冊。這裏用的是網上廣爲流傳《阿里巴巴Java開發手冊》,內容詳細而簡潔,現在已經改編成比較正式的書籍了,叫《碼出高效 Java開發手冊 》。我們選擇幾個基礎的一起瀏覽一下。

(一) 命名風格

  1. 【強制】 代碼中的命名均不能以下劃線或美元符號開始,也不能以下劃線或美元符號結束。
    反例: _name / __name / Object/name/nameObject / 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,導致屬性獲取不到,進而拋出異常。
  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做前綴。
  2. 獲取多個對象的方法用list做前綴。
  3. 獲取統計值的方法用count做前綴。
  4. 插入的方法用save(推薦)或insert做前綴。
  5. 刪除的方法用remove(推薦)或delete做前綴。
  6. 修改的方法用update做前綴。

B) 領域模型命名規約

  1. 數據對象:xxxDO,xxx即爲數據表名。
  2. 數據傳輸對象:xxxDTO,xxx爲業務領域相關的名稱。 3) 展示對象:xxxVO,xxx一般爲網頁名稱。
  3. 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定義。
  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
    正例: (涉及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");
                 // 在右大括號後直接結束,則必須換行
} }

  1. 【強制】單行字符數限制不超過 120 個,超出需要換行,換行時遵循如下原則: 1) 第二行相對第一行縮進 4 個空格,從第三行開始,不再繼續縮進,參考示例。
  1. 運算符與下文一起換行。
  2. 方法調用的點符號與下文一起換行。 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);
  1. 【強制】方法參數在定義和傳入時,多個參數逗號後邊必須加空格。
    正例:下例中實參的"a",後邊必須要有一個空格。 method(“a”, “b”, “c”);
    8.【強制】IDE的text file encoding設置爲UTF-8; IDE中文件的換行符使用Unix格式, 不要使用 windows 格式。
  2. 【推薦】沒有必要增加若干空格來使某一行的字符與上一行對應位置的字符對齊。 正例:
    int a = 3;
    long b = 4L;
    float c = 5F;
    StringBuffer sb = new StringBuffer();
    說明:增加 sb 這個變量,如果需要對齊,則給 a、b、c 都要增加幾個空格,在變量比較多的 情況下,是一種累贅的事情。
  3. 【推薦】方法體內的執行語句組、變量的定義語句組、不同的業務邏輯之間或者不同的語義
    之間插入一個空行。相同業務邏輯和語義之間不需要插入空行。
    說明:沒有必要插入多個空行進行隔開。

(4)控制語句

  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;
    }
  1. 【推薦】除常用方法(如 getXxx/isXxx)等外,不要在條件判斷中執行其它複雜的語句,將復 雜邏輯判斷的結果賦值給一個有意義的布爾變量名,以提高可讀性。
    說明:很多 if 語句內的邏輯相當複雜,閱讀者需要分析條件表達式的最終結果,才能明確什麼樣的條件執行什麼樣的語句,那麼,如果閱讀者分析邏輯表達式錯誤呢?
    正例:
//僞代碼如下
final boolean existed = (file.open(fileName, "w") != null) && (...) || (...);
 if (existed) {
... }

反例:

if ((file.open(fileName, "w") != null) && (...) || (...)) { 
 ...
}
  1. 【推薦】循環體中的語句要考量性能,以下操作儘量移至循環體外處理,如定義對象、變量、獲取數據庫連接,進行不必要的 try-catch 操作(這個 try-catch 是否可以移至循環體外)。
  2. 【推薦】接口入參保護,這種場景常見的是用於做批量操作的接口。
  3. 【參考】下列情形,需要進行參數校驗:
  1. 調用頻次低的方法。
  2. 執行時間開銷很大的方法。此情形中,參數校驗時間幾乎可以忽略不計,但如果因爲參數錯誤導致中間執行回退,或者錯誤,那得不償失。
  3. 需要極高穩定性和可用性的方法。
  4. 對外提供的開放接口,不管是RPC/API/HTTP接口。 5) 敏感權限入口。
  1. 【參考】下列情形,不需要進行參數校驗:
  1. 極有可能被循環調用的方法。但在方法說明裏必須註明外部參數檢查要求。
  2. 底層調用頻度比較高的方法。畢竟是像純淨水過濾的最後一道,參數錯誤不太可能到底層纔會暴露問題。一般 DAO 層與 Service 層都在同一個應用中,部署在同一臺服務器中,所以 DAO 的參數校驗,可以省略。
  3. 被聲明成private只會被自己代碼所調用的方法,如果能夠確定調用方法的代碼傳入參 數已經做過檢查或者肯定不會有問題,此時可以不校驗參數。

(5)註釋規約

  1. 【強制】類、類屬性、類方法的註釋必須使用 Javadoc 規範,使用/*內容/格式,不得使用 //xxx 方式。
    說明:在 IDE 編輯窗口中,Javadoc 方式會提示相關注釋,生成 Javadoc 可以正確輸出相應注 釋;在 IDE 中,工程調用方法時,不進入方法即可懸浮提示方法、參數、返回值的意義,提高 閱讀效率。
  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 標記某代碼是錯誤的,而且不能工作,需要及時糾正的情況。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章