Java權威編碼規範

来源:http://www.cnblogs.com/Dylansuns/archive/2017/06/07/6959442.html
-Advertisement-
Play Games

一、編程規約 (一) 命名規約 1. 【強制】 代碼中的命名均不能以下劃線或美元符號開始,也不能以下劃線或美元符號結束。 反例: _nam / __name / $Object / name_ / name$ / Object$2. 【強制】 代碼中的命名嚴禁使用拼音與英文混合的方式,更不允許直接使 ...


一、編程規約

(一) 命名規約

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

  反例: _nam / __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. 【強制】抽象類命名使用Abstract或Base開頭;異常類命名使用Exception結尾;測試類命名以它要測試的類的名稱開始,以Test結尾。
7. 【強制】中括弧是數組類型的一部分,數組定義如下:String[] args;

  反例:請勿使用String args[]的方式來定義。
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)【強制】對於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一般為網頁名稱。

    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定義。
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 設置 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. 【推薦】方法體內的執行語句組、變數的定義語句組、不同的業務邏輯之間或者不同的語義之間插入一個空行。相同業務邏輯和語義之間不需要插入空行。

  說明:沒有必要插入多行空格進行隔開。

(四) 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風險。

  反例:比如顯示成交總額漲跌情況,即正負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 data + 100;
        } else {
            return data - 100;
        }
    }

17. 【推薦】迴圈體內,字元串的聯接方式,使用StringBuilder的append方法進行擴展。 反例:

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. 【推薦】慎用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的成員變數,刪除一下,不得手心冒點汗嗎?變數像自己的小孩,儘量在自己的視線內,變數作用域太大,如果無限制的到處跑,那麼你會擔心的。

(五) 集合處理

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無參方法存在問題,此方法返回值只能是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)也會隨之修改。
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;
    }
}

9. 【推薦】集合初始化時,儘量指定集合初始值大小。 說明:ArrayList儘量使用ArrayList(int initialCapacity) 初始化。

10. 【推薦】使用entrySet遍歷Map類集合KV,而不是keySet方式進行遍歷。

  說明:keySet其實是遍歷了2次,一次是轉為Iterator對象,另一次是從hashMap中取出key所對應的value。而entrySet只是遍歷了一次就把key和value都放到了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)帶來的負面影響。

  說明:穩定性指集合每次遍歷的元素次序是一定的。有序性是指遍歷的結果是按某種比較規則依次排列的。如:ArrayList是order/unsort;HashMap是unorder/unsort;TreeSet是order/sort。
13. 【參考】利用Set元素唯一的特性,可以快速對一個集合進行去重操作,避免使用List的contains方法進行遍歷、對比、去重操作。

(六) 併發處理

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

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

  正例:

public class TimerTaskThread extends Thread {
    public TimerTaskThread(){
    super.setName("TimerTaskThread"); 
    ...
}

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

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

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

  說明: Executors返回的線程池對象的弊端如下 :

  1)FixedThreadPool和 SingleThread:

    允許的請求隊列長度為 Integer.MAX_VALUE,可能會堆積大量的請求,從而導致 OOM。

  2)CachedThreadPool和 ScheduledThreadPool: 允許的創建線程數量為 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 strong immutable thread-safe。

6. 【強制】高併發時,同步調用應該去考量鎖的性能損耗。能用無鎖數據結構,就不要用鎖;能鎖區塊,就不要鎖整個方法體;能用對象鎖,就不要用類鎖。
7. 【強制】對多個資源、資料庫表、對象同時加鎖時,需要保持一致的加鎖順序,否則可能會造成死鎖。

  說明:線程一需要對錶A、B、C依次全部加鎖後才可以進行更新操作,那麼線程二的加鎖順序也必須是A、B、C,否則可能出現死鎖。
8. 【強制】併發修改同一記錄時,避免更新丟失,要麼在應用層加鎖,要麼在緩存加鎖,要麼在資料庫層使用樂觀鎖,使用version作為更新依據。

  說明:如果每次訪問衝突概率小於20%,推薦使用樂觀鎖,否則使用悲觀鎖。樂觀鎖的重試次數不得小於3次。
9. 【強制】多線程並行處理定時任務時,Timer運行多個TimeTask時,只要其中之一沒有捕獲拋出的異常,其它任務便會自動終止運行,使用ScheduledExecutorService則沒有這個問題。
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修飾。這個變數是針對一個線程內所有操作共有的,所以設置為靜態變數,所有此類實例共用此靜態變數 ,也就是說在類第一次被使用時裝載,只分配一塊存儲空間,所有此類的對象(只要是這個線程內定義的)都可以操控這個變數。

(七) 控制語句

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) 執行時間開銷很大的方法,參數校驗時間幾乎可以忽略不計,但如果因為參數錯誤導致中間執行回退,或者錯誤,那得不償失。
  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. 【推薦】代碼修改的同時,註釋也要進行相應的修改,尤其是參數、返回值、異常、核心邏輯等的修改。

  說明:代碼與註釋更新不同步,就像路網與導航軟體更新不同步一樣,如果導航軟體嚴重滯後,就失去了導航的意義。
8. 【參考】註釋掉的代碼儘量要配合說明,而不是簡單的註釋掉。

  說明:代碼被註釋掉有兩種可能性:

    1)後續會恢復此段代碼邏輯。

    2)永久不用。前者如果沒有備註信息,難以知曉註釋動機。後者建議直接刪掉(代碼倉庫保存了歷史代碼)。
9. 【參考】對於註釋的要求:

  第一、能夠準確反應設計思想和代碼邏輯;

  第二、能夠描述業務含義,使別的程式員能夠迅速瞭解到代碼背後的信息。完全沒有註釋的大段代碼對於閱讀者形同天書,註釋是給自己看的,即使隔很長時間,也能清晰理解當時的思路;註釋也是給繼任者看的,使其能夠快速接替自己的工作。
10. 【參考】好的命名、代碼結構是自解釋的,註釋力求精簡準確、表達到位。避免出現註釋的一個極端:過多過濫的註釋,代碼的邏輯一旦修改,修改註釋是相當大的負擔。

反例:
  // put elephant into fridge
  put(elephant, fridge);
方法名put,加上兩個有意義的變數名elephant和fridge,已經說明瞭這是在乾什麼,語義清晰的代碼不需要額外的註釋。
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方法。
5. 【強制】獲取當前毫秒數System.currentTimeMillis(); 而不是new Date().getTime();

  說明:如果想獲取更加精確的納秒級時間值,用System.nanoTime()。在JDK8中,針對統計時間等場景,推薦使用Instant類。
6. 【推薦】儘量不要在vm中加入變數聲明、邏輯運算符,更不要在vm模板中加入任何複雜的邏輯。
7. 【推薦】任何數據結構的構造或初始化,都應指定大小,避免數據結構無限增長吃光記憶體。
8. 【推薦】對於“明確停止使用的代碼和配置”,如方法、變數、類、配置文件、動態配置屬性等要堅決從程式中清理出去,避免造成過多垃圾。

二、異常日誌

(一) 異常處理

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,自動解箱拋NPE。

  2) 資料庫的查詢結果可能為null。

  3) 集合里的元素即使isNotEmpty,取出的數據元素也可能為null。
  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. 【參考】避免出現重覆的代碼(Don’t Repeat Yourself),即DRY原則。 說明:隨意複製和粘貼代碼,必然會導致代碼的重覆,在以後需要修改時,需要修改所有的副本,容易遺漏。必要時抽取共性方法,或者抽象公共類,甚至是共用模塊。

  正例:一個類中有多個public方法,都需要進行數行相同的參數校驗操作,這個時候請抽取:
  private boolean checkParam(DTO dto){...}

(二) 日誌規約

1. 【強制】應用中不可直接使用日誌系統(Log4j、Logback)中的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.log。logType:日誌類型,推薦分類有stats/desc/monitor/visit等;logName:日誌描述。這種命名的好處:通過文件名就可知道日誌文件屬於什麼應用,什麼類型,什麼目的,也有利於歸類查找。

  正例:mppserver應用中單獨監控時區轉換異常,如: mppserver_monitor_timeZoneConvert.log 說明:推薦對日誌進行分類,錯誤日誌和業務日誌儘量分開存放,便於開發人員查看,也便於通過日誌對系統進行及時監控。
4. 【強制】對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);
5. 【強制】避免重覆列印日誌,浪費磁碟空間,務必在log4j.xml中設置additivity=false。

  正例:<logger name="com.taobao.dubbo.config" additivity="false">
6. 【強制】異常信息應該包括兩類信息:案發現場信息和異常堆棧信息。如果不處理,那麼往上拋。

  正例:logger.error(各類參數或者對象toString + "_" + e.getMessage(), e);
7. 【推薦】可以使用warn日誌級別來記錄用戶輸入參數錯誤的情況,避免用戶投訴時,無所適從。註意日誌輸出的級別,error級別隻記錄系統邏輯出錯、異常等重要的錯誤信息。如非必要,請不要在此場景打出error級別。
8. 【推薦】謹慎地記錄日誌。生產環境禁止輸出debug日誌;有選擇地輸出info日誌;如果使用warn來記錄剛上線時的業務行為信息,一定要註意日誌輸出量的問題,避免把伺服器磁碟撐爆,並記得及時刪除這些觀察日誌。

  說明:大量地輸出無效日誌,不利於系統性能提升,也不利於快速定位錯誤點。記錄日誌時請思考:這些日誌真的有人看嗎?看到這條日誌你能做什麼?能不能給問題排查帶來好處?

三、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. 【強制】禁用保留字,如desc、range、match、delayed等,請參考MySQL官方保留字。
5. 【強制】唯一索引名為uk_欄位名;普通索引名則為idx_欄位名。 說明:uk_ 即 unique key;idx_ 即index的簡稱。
6. 【強制】小數類型為decimal,禁止使用float和double。

  說明:float和double在存儲的時候,存在精度損失的問題,很可能在值的比較時,得到不正確的結果。如果存儲的數據範圍超過decimal的範圍,建議將數據拆成整數和小數分開存儲。
7. 【強制】如果存儲的字元串長度幾乎相等,使用char定長字元串類型。
8. 【強制】varchar是可變長字元串,不預先分配存儲空間,長度不要超過5000,如果存儲長度大於此值,定義欄位類型為text,獨立出來一張表,用主鍵來對應,避免影響其它欄位索引效率。
9. 【強制】表必備三欄位:id, gmt_create, gmt_modified。

  說明:其中id必為主鍵,類型為unsigned bigint、單表時自增、步長為1。gmt_create, gmt_modified的類型均為date_time類型。
10. 【推薦】表的命名最好是加上“業務名稱_表的作用”。 正例:tiger_task / tiger_reader / mpp_config
11. 【推薦】庫名與應用名稱儘量一致。
12. 【推薦】如果修改欄位含義或對欄位表示的狀態追加時,需要及時更新欄位註釋。
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無法排序。
6. 【推薦】利用覆蓋索引來進行查詢操作,來避免回表操作。

  說明:如果一本書需要知道第11章是什麼標題,會翻開第11章對應的那一頁嗎?目錄瀏覽一下就好,這個目錄就是起到覆蓋索引的作用。

  正例:能夠建立索引的種類:主鍵索引、唯一索引、普通索引,而覆蓋索引是一種查詢的一種效果,用explain的結果,extra列會出現:using index。
7. 【推薦】利用延遲關聯或者子查詢優化超多分頁場景。

  說明: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
8. 【推薦】 SQL性能優化的目標:至少要達到 range 級別,要求是ref級別,如果可以是consts最好。

  說明:

    1)consts 單表中最多只有一個匹配行(主鍵或者唯一索引),在優化階段即可讀取到數據。

    2)ref 指的是使用普通的索引(normal index)。

    3)range 對索引進行範圍檢索。

  反例:explain表的結果,type=index,索引物理文件全掃描,速度非常慢,這個index級別比較range還低,與全表掃描是小巫見大巫。
9. 【推薦】建組合索引的時候,區分度最高的在最左邊。 正例:如果where a=? and b=? ,a列的幾乎接近於唯一值,那麼只需要單建idx_a索引即可。

  說明:存在非等號和等號混合判斷條件時,在建索引時,請把等號條件的列前置。如:where a>? and b=? 那麼即使a的區分度更高,也必須把b放在索引的最前列。
10. 【參考】創建索引時避免有如下極端誤解:

  1)誤認為一個查詢就需要建一個索引。

  2)誤認為索引會消耗空間、嚴重拖慢更新和新增速度。

  3)誤認為唯一索引一律需要在應用層通過“先查後插”方式解決。

(三) SQL規約

1. 【強制】不要使用count(列名)或count(常量)來替代count(*),count(*)就是SQL92定義的標準統計行數的語法,跟資料庫無關,跟NULL和非NULL無關。

  說明:count(*)會統計值為NULL的行,而count(列名)不會統計此列為NULL值的行。
2. 【強制】count(distinct col) 計算該列除NULL之外的不重覆數量。註意 count(distinct col1, col2) 如果其中一列全為NULL,那麼即使另一列有不同的值,也返回為0。
3. 【強制】當某一列的值全是NULL時,count(col)的返回結果為0,但sum(col)的返回結果為NULL,因此使用sum()時需註意NPE問題。

  正例:可以使用如下方式來避免sum的NPE問題:SELECT IF(ISNULL(SUM(g)),0,SUM(g)) FROM table;
4. 【強制】使用ISNULL()來判斷是否為NULL值。註意:NULL與任何值的直接比較都為NULL。

  說明: 1) NULL<>NULL的返回結果是NULL,而不是false。

      2) NULL=NULL的返回結果是NULL,而不是true。

        3) NULL<>1的返回結果是NULL,而不是true。
5. 【強制】 在代碼中寫分頁查詢邏輯時,若count為0應直接返回,避免執行後面的分頁語句。
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編碼的區別。
11. 【參考】 TRUNCATE TABLE 比 DELETE 速度快,且使用的系統和事務日誌資源少,但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語句的所有記錄,再通過subList取start,size的子集合,線上因為這個原因曾經出現過OOM。
正例:在sqlmap.xml中引入 #start#, #size#
  Map<String, Object> map = new HashMap<String, Object>();
  map.put("start", start);
  map.put("size", size);
6. 【強制】不允許直接拿HashMap與Hashtable作為查詢結果集的輸出。
7. 【強制】更新數據表記錄時,必須同時更新記錄對應的gmt_modified欄位值為當前時間。
8. 【推薦】不要寫一個大而全的數據更新介面,傳入為POJO類,不管是不是自己的目標更新欄位,都進行update table set c1=value1,c2=value2,c3=value3; 這是不對的。執行SQL時,儘量不要更新無改動的欄位,一是易出錯;二是效率低

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 註意: 轉載自併發編程網 – ifeve.com本文鏈接地址: Java NIO系列教程(一) Java NIO 概述 JAVA-1NIO概述 Java NIO 由以下幾個核心部分組成: Channels Buffers Selectors 雖然Java NIO 中除此之外還有很多類和組件,但在我看 ...
  • idea啟動TOMCAT html 亂碼,在運行/調試 配置對話框的Startup/Connection面板中,勾選Pass environment variables.並添加一個environment variable,Name填 JAVA_TOOL_OPTIONS, Value填 -Dfile.... ...
  • 作業:購物商城 商品展示,價格 買,加入購物車 付款,錢不夠 流程圖如下: 代碼共有4個文件,如下: 用戶文件: 商品文件: 購物車文件: 錢包文件: 代碼如下: 上述代碼運行流程如下: (1)展示商品信息; (2)用戶登錄驗證; (3)用戶輸入想購買產品及數量,輸入quit退出購物; (4)添加到 ...
  • 實際項目中pom.xml依賴寫法: Maven 安裝 JAR 包的命令是: 例如我的這個spring-context-support-3.1.0.RELEASE.jar 文件放在了"D:\mvn\"中 則命令為(在cmd中執行): mvn install:install-file -Dfile=D: ...
  • 基於51單片機的萬年曆,用到了單片機獨立鍵盤、數位管、LED燈模塊實現。 想要簡單還是DS1302好用。 -- -- -- 參考: http://www.cnblogs.com/LXSYD-C/p/6364888.html 如有錯誤還請指出,如有侵權還請告知,如需轉載請註明出處! 本人博客:http ...
  • 給人搬了十幾個網站,老站用西部數位建站助手創建的,現在過期了無法繼續創建,只能在Internet 信息服務(IIS)管理器創建網站,創建下來都沒問題,但是就是無法打開網站。 測試打開txt文檔、靜態頁面都能打開,一到打開php文件就直接就掛了,無法打開,什麼報錯都沒有。 之前有用iis6以外的伺服器 ...
  • 嘗試用springmvc,mybatis,mysql做個工具平臺。 在本地mac筆記本上運行正常,但把包放置到伺服器上,啟動tomcat就報錯。類找不到了。 文件目錄: 實現需求:上傳文檔並記錄在資料庫中。自建了DocFile類。創建對應的mapper文件寫sql語句。 mapper.xml中nam ...
  • 通常我們的做法是(尤其是在學習階段):定義一個新的變數,藉助它完成交換。代碼如下: int a,b; a=10; b=15; int t; t=a; a=b; b=t; 這種演算法易於理解,特別適合幫助初學者瞭解電腦程式的特點,是賦值語句的經典應用。在實際軟體開發當中,此演算法簡單明瞭,不會產生歧義, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...