阿里巴巴Java開發規約

命名風格

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

【強制】代碼中的命名嚴禁使用拼音與英文混合的方式,更不允許直接使用中文的方式。

說明:正確的英文拼寫和語法可以讓閱讀者易於理解,避免歧義。注意,即使純拼音命名方式也要避免採用。

【強制】類名使用UpperCamelCase風格,但以下情形例外:DO / BO / DTO / VO / AO / PO等。

正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion
反例:macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion
分層領域模型規約:
DO( Data Object):與數據庫表結構一一對應,通過DAO層向上傳輸數據源對象。
DTO( Data Transfer Object):數據傳輸對象,Service或Manager向外傳輸的對象。
BO( Business Object):業務對象。 由Service層輸出的封裝業務邏輯的對象。
AO( Application Object):應用對象。 在Web層與Service層之間抽象的複用對象模型,極爲貼近展示層,複用度不高。
VO( View Object):顯示層對象,通常是Web向模板渲染引擎層傳輸的對象。
POJO( Plain Ordinary Java Object):在本手冊中, POJO專指只有setter/getter/toString的簡單類,包括DO/DTO/BO/VO等。
Query:數據查詢對象,各層接收上層的查詢請求。 注意超過2個參數的查詢封裝,禁止使用Map類來傳輸。

領域模型命名規約:
數據對象:xxxDO,xxx即爲數據表名。
數據傳輸對象:xxxDTO,xxx爲業務領域相關的名稱。
展示對象:xxxVO,xxx一般爲網頁名稱。
POJO是DO/DTO/BO/VO的統稱,禁止命名成xxxPOJO。

【強制】方法名、參數名、成員變量、局部變量都統一使用lowerCamelCase風格,必須遵從駝峯形式。

正例: localValue / getHttpMessage() / inputUserId

【強制】常量命名全部大寫,單詞間用下劃線隔開,力求語義表達完整清楚,不要嫌名字長。

正例:MAX_STOCK_COUNT 反例:MAX_COUNT

【強制】抽象類命名使用Abstract或Base開頭;異常類命名使用Exception結尾;測試類命名以它要測試的類名開始,以Test結尾

【強制】類型與中括號緊挨相連來定義數組。

正例: 定義整形數組 int[] arrayDemo; int[] arrayDemo;
反例: 在 main 參數中,使用 String args[] 來定義

【強制】POJO(簡單的Java對象,實際就是普通JavaBeans)類中布爾類型的變量,都不要加is前綴,否則部分框架解析會引起序列化錯誤。

反例:定義爲基本數據類型Boolean isDeleted;的屬性,它的方法也是isDeleted(),RPC
框架在反向解析的時候,“誤以爲”對應的屬性名稱是deleted,導致屬性獲取不到,進而拋出異常。

【強制】包名統一使用小寫,點分隔符之間有且僅有一個自然語義的英語單詞。包名統一使用單數形式,但是類名如果有複數含義,類名可以使用複數形式。

正例:應用工具類包名爲com.alibaba.ai.util、類名爲MessageUtils(此規則參考spring的框架結構)

【強制】杜絕完全不規範的縮寫,避免望文不知義。

反例:AbstractClass“縮寫”命名成AbsClass;condition“縮寫”命名成 condi,此類隨意縮寫嚴重降低了代碼的可閱讀性。

【推薦】爲了達到代碼自解釋的目標,任何定義編程元素在命名時使用盡量完整單詞 組合來表達其意。

正例: 從遠程 倉庫 拉取代碼的類 命名 爲 PullCodeFromRemoteRepository 。 反例: 變量 int a;  的隨意命名 方式。

【推薦】如果模塊、接口、類、方法使用了設計模式,在命名時體現出具體模式,將設計模式體現在名字中,有利於閱讀者快速理解架構設計理念

正例:
public class OrderFactory; 
public class LoginProxy; 
public class ResourceObserver;

【推薦】接口類中的方法和屬性不要加任何修飾符號(public 也不要加),保持代碼的簡潔性,並加上有效的Javadoc註釋。儘量不要在接口裏定義變量,如果一定要定義變量,肯定是與接口方法相關,並且是整個應用的基礎常量。

正例:接口方法簽名void f();
接口基礎常量String COMPANY = "alibaba"; 
反例:接口方法定義public abstract void f(); 
說明:JDK8中接口允許有默認實現,那麼這個default方法,是對所有實現類都有價值的默認實現。

接口和實現類的命名有兩套規則:

  • 【強制】對於Service和DAO類,基於SOA的理念,暴露出來的服務一定是接口,內部的實現類用Impl的後綴與接口區別
正例:CacheServiceImpl實現CacheService接口。
  • 【推薦】 如果是形容能力的接口名稱,取對應的形容詞爲接口名(通常是–able的形式)
正例:AbstractTranslator實現 Translatable。

【參考】枚舉類名建議帶上Enum後綴,枚舉成員名稱需要全大寫,單詞間用下劃線隔開。

說明:枚舉其實就是特殊的常量類,且構造方法被默認強制是私有。 
正例:枚舉名字爲ProcessStatusEnum的成員名稱:SUCCESS / UNKNOWN_REASON。

【參考】各層命名規約:

Service/DAO層方法命名規約 
1) 獲取單個對象的方法用get作前綴。
2) 獲取多個對象的方法用list作前綴。 
3) 獲取統計值的方法用count作前綴。 
4) 插入的方法用save/insert作前綴。 
5) 刪除的方法用remove/delete作前綴。
6) 修改的方法用update作前綴。

領域模型命名規約
1) 數據對象:xxxDO,xxx即爲數據表名。
2) 數據傳輸對象:xxxDTO,xxx爲業務領域相關的名稱。 
3) 展示對象:xxxVO,xxx一般爲網頁名稱。
4) POJO是DO/DTO/BO/VO的統稱,禁止命名成xxxPOJO。

常量定義

【強制】不允許任何魔法值(即未經預先定義的常量)直接出現在代碼中。

反例:String key = "Id#taobao_" + tradeId; cache.put(key, value);

【強制】long或者Long初始賦值時,使用大寫的L,不能是小寫的l,小寫容易跟數字1混淆,造成誤解。

說明:Long a = 2l; 寫的是數字的21,還是Long型的2?

【推薦】不要使用一個常量類維護所有常量,按常量功能進行歸類,分開維護。

說明:大而全的常量類,非得使用查找功能才能定位到修改的常量,不利於理解和維護。
正例:緩存相關常量放在類CacheConsts下;系統配置相關常量放在類ConfigConsts下。

【推薦】如果變量值僅在一個固定範圍內變化用enum類型來定義。

說明:如果存在名稱之外的延伸屬性使用enum類型,下面正例中的數字就是延伸信息,表示一年中的第幾個季節。
正例: 
public enum SeasonEnum { 
	SPRING(1), SUMMER(2), AUTUMN(3), WINTER(4); 
	int seq; 
	SeasonEnum(int seq){ this.seq = seq; 
	}
}

代碼格式

【強制】大括號的使用約定。如果是大括號內爲空,則簡潔地寫成{}即可,不需要換行;如果是非空代碼塊則:

  • 左大括號前不換行。
  • 左大括號後換行。
  • 右大括號前換行。
  • 右大括號後還有else等代碼則不換行;
  • 表示終止的右大括號後必須換行。

【強制】 左小括號和字符之間不出現空格;同樣,右小括號和字符之間也不出現空格。詳見第5條下方正例提示

【強制】if/for/while/switch/do等保留字與括號之間都必須加空格

【強制】任何二目、三目運算符的左右兩邊都需要加一個空格。

說明:運算符包括賦值運算符=、邏輯運算符&&、加減乘除符號等。

【強制】註釋的雙斜線與內容之間有且僅一個空格。

正例: // 這是示例註釋,請注意在雙斜線之後有一個空格 String ygb = new String();

【強制】單行字符數限不超過 120 個,超出需要換行時 遵循如下原則:

  • 第二行相對一縮進 4個空格,從第三行開始不再繼續縮進參考示例。
  • 運算符與下文一起換行。
  • 方法調用的點符號與下文一起換行。
  • 方法調用時,多個參數,需要換行時,在逗號後進行。
  • 在括號前不要換行,見反例。
正例:
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);

【強制】方法參數在定義和傳入時,多個參數逗號後邊必須加空格。

正例:下例中實參的"a",後邊必須要有一個空格。
method("a", "b", "c");

【強制】IDE的text file encoding設置爲UTF-8; IDE中文件的換行符使用Unix格式,不要使用Windows格式。

【推薦】沒有必要增加若干空格來使某一行的字符與上一行對應位置的字符對齊。

正例:
int a = 3;
long b = 4L;
float c = 5F;
StringBuffer sb = new StringBuffer();
說明:增加sb這個變量,如果需要對齊,則給a、b、c都要增加幾個空格,在變量比較多的情況下,是非常累贅的事情。

【推薦】不同邏輯、不同語義、不同業務的代碼之間插入一個空行分隔開來以提升可讀性。 說明:沒有必要插入多個空行進行隔開。

OOP規約

【強制】避免通過一個類的對象引用訪問此類的靜態變量或靜態方法,無謂增加編譯器解析成本,直接用類名來訪問即可

【強制】所有的覆寫方法,必須加@Override註解。

【強制】外部正在調用或者二方庫依賴的接口,不允許修改方法簽名,避免對接口調用方產生影響。接口過時必須加@Deprecated註解,並清晰地說明採用的新接口或者新服務是什麼。

【強制】不能使用過時的類或方法。

【強制】Object的equals方法容易拋空指針異常,應使用常量或確定有值的對象來調用equals。

正例:"test".equals(object); 
反例:object.equals("test");
說明:推薦使用java.util.Objects#equals(JDK7引入的工具類)

【強制】所有的相同類型的包裝類對象之間值的比較,全部使用equals方法比較。

說明:
對於Integer var = ? 在-128至127範圍內的賦值,Integer對象是在IntegerCache.cache產生,會複用已有對象,這個區間內的Integer值可以直接使用==進行判斷,但是這個區間之外的所有數據,都會在堆上產生,並不會複用已有對象,這是一個大坑,推薦使用equals方法進行判斷。

【推薦】當一個類有多個構造方法,或者多個同名方法,這些方法應該按順序放置在一起,便於閱讀,此條規則優先於第15條規則。

【推薦】 類內方法定義的順序依次是:公有方法或保護方法 > 私有方法 > getter/setter方法。

說明:公有方法是類的調用者和維護者最關心的方法,首屏展示最好;保護方法雖然只是子類關心,也可能是“模板設計模式”下的核心方法;而私有方法外部一般不需要特別關心,是一個黑盒實現;因爲承載的信息價值較低,所有Service和DAO的getter/setter方法放在類體最後。

【推薦】setter方法中,參數名稱與類成員變量名稱一致,this.成員名 = 參數名。在getter/setter方法中,不要增加業務邏輯,增加排查問題的難度

【推薦】循環體內,字符串的連接方式,使用StringBuilder的append方法進行擴展。

說明:反編譯出的字節碼文件顯示每次循環都會new出一個StringBuilder對象,然後進行append操作,最後通過toString方法返回String對象,造成內存資源浪費。

反例:
String str = "start";
for (int i = 0; i < 100; i++) {
  str = str + "hello";
}

【推薦】類成員與方法訪問控制從嚴:

控制語句

【強制】在一個switch塊內,每個case要麼通過break/return等來終止,要麼註釋說明程序將繼續執行到哪一個case爲止;在一個switch塊內,都必須包含一個default語句並且放在最後,即使空代碼。

【強制】在if/else/for/while/do語句中必須使用大括號。即使只有一行代碼,避免採用

【強制】在高併發場景中,避免使用 ”等於 ”判斷作爲中或退出的條件。 判斷作爲中或退出的條件。

說明: 如果併發控制沒有處理好,容易產生等值判斷被 如果併發控制沒有處理好,容易產生等值判斷被 “擊穿 ”的情況,使用大於或小區間 的情況,使用大於或小區間 的情況,使用大於或小區間 的情況,使用大於或小區間 判斷條件來代替。 反例: 判斷剩餘獎品數量等於 0時,終止發放獎品但因爲並處理錯誤導致數量瞬間變 時,終止發放獎品但因爲並處理錯誤導致數量瞬間變 時,終止發放獎品但因爲並處理錯誤導致數量瞬間變 成了負數, 這樣的話,活動無法終止。

【推薦】表達異常的分支時,少用if-else方式,這種方式可以改寫成

if (condition) {
...
return obj;
}
// 接着寫else的業務邏輯代碼;
說明:如果非得使用if()...else if()...else...方式表達邏輯,【強制】避免後續代碼維護困難,請勿超過3層。

【推薦】除常用方法(如getXxx/isXxx)等外,不要在條件判斷中執行其它複雜的語句,將複雜邏輯判斷的結果賦值給一個有意義的布爾變量名,以提高可讀性。

說明:很多if語句內的邏輯相當複雜,閱讀者需要分析條件表達式的最終結果,才能明確什麼樣的條件執行什麼樣的語句,那麼,如果閱讀者分析邏輯表達式錯誤呢? 正例:
// 僞代碼如下
final boolean existed = (file.open(fileName, "w") != null) && (...) || (...);
if (existed) {
...
}
反例: if ((file.open(fileName, "w") != null) && (...) || (...)) {
...
}

【推薦】循環體中的語句要考量性能,以下操作儘量移至循環體外處理,如定義對象、變量、獲取數據庫連接,進行不必要的try-catch操作(這個try-catch是否可以移至循環體外)。

【推薦】避免採用取反邏輯運算符。

說明: 取反邏輯不利於快速理解,並且寫法必然存在對應的正向。
正例: 使用 if (x < 628) if (x < 628) if (x < 628) 來表達 x 小於 628 。
反例: 使用 if (!(x >= 628)) if (!(x >= 628)) if (!(x >= 628)) 來表達 x 小於 628 。

【參考】下列情形,需要進行參數校驗:

  • 調用頻次低的方法。
  • 執行時間開銷很大的方法。此情形中,參數校驗時間幾乎可以忽略不計,但如果因爲參數錯誤導致中間執行回退,或者錯誤,那得不償失。
  • 需要極高穩定性和可用性的方法。
  • 對外提供的開放接口,不管是RPC/API/HTTP接口。
  • 敏感權限入口。

【參考】下列情形,不需要進行參數校驗:

  • 極有可能被循環調用的方法。但在方法說明裏必須註明外部參數檢查要求。
  • 底層調用頻度比較高的方法。畢竟是像純淨水過濾的最後一道,參數錯誤不太可能到底層纔會暴露問題。一般DAO層與Service層都在同一個應用中,部署在同一臺服務器中,所以DAO的參數校驗,可以省略。
  • 被聲明成private只會被自己代碼所調用的方法,如果能夠確定調用方法的代碼傳入參數已經做過檢查或者肯定不會有問題,此時可以不校驗參數

註釋規約

【強制】類、類屬性、類方法的註釋必須使用Javadoc規範,使用/*內容/格式,不得使用// xxx方式。

說明:在IDE編輯窗口中,Javadoc方式會提示相關注釋,生成Javadoc可以正確輸出相應註釋;在IDE中,工程調用方法時,不進入方法即可懸浮提示方法、參數、返回值的意義,提高閱讀效率。

【強制】所有的類都必須添加創建者和創建日期。

【強制】方法內部單行註釋,在被註釋語句上方另起一行,使用//註釋。方法內部多行註釋使用/* */註釋,注意與代碼對齊。

【強制】所有的枚舉類型字段必須要有註釋,說明每個數據項的用途。

【推薦】與其“半吊子”英文來註釋,不如用中文註釋把問題說清楚。專有名詞與關鍵字保持英文原文即可。

反例:“TCP連接超時”解釋成“傳輸控制協議連接超時”,理解反而費腦筋。

【推薦】代碼修改的同時,註釋也要進行相應的修改,尤其是參數、返回值、異常、核心邏輯等的修改。

說明:代碼與註釋更新不同步,就像路網與導航軟件更新不同步一樣,如果導航軟件嚴重滯後,就失去了導航的意義。

【參考】謹慎註釋掉代碼。在上方詳細說明,而不是簡單地註釋掉。如果無用,則刪除。

說明:代碼被註釋掉有兩種可能性:
1)後續會恢復此段代碼邏輯。
2)永久不用。前者如果沒有備註信息,難以知曉註釋動機。後者建議直接刪掉(代碼倉庫保存了歷史代碼)。

【參考】對於註釋的要求:

  • 第一、能夠準確反應設計思想和代碼邏輯;
  • 第二、能夠描述業務含義,使別的程序員能夠迅速瞭解到代碼背後的信息。完全沒有註釋的大段代碼對於閱讀者形同天書,註釋是給自己看的,即使隔很長時間,也能清晰理解當時的思路;註釋也是給繼任者看的,使其能夠快速接替自己的工作。

【參考】好的命名、代碼結構是自解釋的,註釋力求精簡準確、表達到位。避免出現註釋的一個極端:過多過濫的註釋,代碼的邏輯一旦修改,修改註釋是相當大的負擔。

反例:
// put elephant into fridge
put(elephant, fridge);
方法名put,加上兩個有意義的變量名elephant和fridge,已經說明了這是在幹什麼,語義清晰的代碼不需要額外的註釋。

【參考】特殊註釋標記,請註明標記人與標記時間。注意及時處理這些標記,通過標記掃描,經常清理此類標記。線上故障有時候就是來源於這些標記處的代碼。

  • 待辦事宜(TODO):( 標記人,標記時間,[預計處理時間]) 表示需要實現,但目前還未實現的功能。這實際上是一個Javadoc的標籤,目前的Javadoc還沒有實現,但已經被廣泛使用。只能應用於類,接口和方法(因爲它是一個Javadoc標籤)。
  • 錯誤,不能工作(FIXME):(標記人,標記時間,[預計處理時間]) 在註釋中用FIXME標記某代碼是錯誤的,而且不能工作,需要及時糾正的情況。

異常處理

【強制】Java 類庫中定義的可以通過預檢查方式規避的RuntimeException異常不應該通過catch 的方式來處理,比如:NullPointerException,IndexOutOfBoundsException等等。

說明:無法通過預檢查的異常除外,比如,在解析字符串形式的數字時,不得不通過catch NumberFormatException來實現。 
正例:if (obj != null) {...}
反例:try { obj.method() } catch (NullPointerException e) {…}

【強制】異常不要用來做流程控制,條件控制。

說明:異常設計的初衷是解決程序運行中的各種意外情況,且異常的處理效率比條件判斷方式要低很多。

【強制】catch時請分清穩定代碼和非穩定代碼,穩定代碼指的是無論如何不會出錯的代碼。對於非穩定代碼的catch儘可能進行區分異常類型,再做對應的異常處理。

說明:對大段代碼進行try-catch,使程序無法根據不同的異常做出正確的應激反應,也不利於定位問題,這是一種不負責任的表現。

【強制】捕獲異常是爲了處理它,不要捕獲了卻什麼都不處理而拋棄之,如果不想處理它,請將該異常拋給它的調用者。最外層的業務使用者,必須處理異常,將其轉化爲用戶可以理解的內容.

【強制】有try塊放到了事務代碼中,catch異常後,如果需要回滾事務,一定要注意手動回滾事務。

【強制】捕獲異常與拋異常,必須是完全匹配,或者捕獲異常是拋異常的父類

【推薦】方法的返回值可以爲null,不強制返回空集合,或者空對象等,必須添加註釋充分說明什麼情況下會返回null值。

【推薦】防止NPE(編程語言中的空指針異常),是程序員的基本修養,注意NPE產生的場景:

  • 返回類型爲基本數據類型,return包裝數據類型的對象時,自動拆箱有可能產生NPE。 反例:public int f() { return Integer對象}, 如果爲null,自動解箱拋NPE。
  • 數據庫的查詢結果可能爲null。
  • 集合裏的元素即使isNotEmpty,取出的數據元素也可能爲null。
  • 遠程調用返回對象時,一律要求進行空指針判斷,防止NPE。
  • 對於Session中獲取的數據,建議NPE檢查,避免空指針。
  • 級聯調用obj.getA().getB().getC();一連串調用,易產生NPE。
  • 正例:使用JDK8的Optional類來防止NPE問題

【推薦】定義時區分unchecked / checked 異常,避免直接拋出new RuntimeException(),更不允許拋出Exception或者Throwable,應使用有業務含義的自定義異常。推薦業界已定義過的自定義異常,如:DAOException / ServiceException等。

【參考】對於公司外的http/api開放接口必須使用“錯誤碼”;而應用內部推薦異常拋出;跨應用間RPC調用優先考慮使用Result方式,封裝isSuccess()方法、“錯誤碼”、“錯誤簡短信息”。

【參考】避免出現重複的代碼(Don’t Repeat Yourself),即DRY原則。

說明:隨意複製和粘貼代碼,必然會導致代碼的重複,在以後需要修改時,需要修改所有的副本,容易遺漏。必要時抽取共性方法,或者抽象公共類,甚至是組件化。
正例:一個類中有多個public方法,都需要進行數行相同的參數校驗操作,這個時候請抽取:
private boolean checkParam(DTO dto) {...}

日誌規約

【強制】應用中不可直接使用日誌系統(Log4j、Logback)中的API,而應依賴使用日誌框架SLF4J中的API,使用門面模式的日誌框架,有利於維護和各個類的日誌處理方式統一。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Abc.class);

【強制】日誌文件推薦至少保存15天,因爲有些異常具備以“周”爲頻次發生的特點。

【強制】應用中的擴展日誌(如打點、臨時監控、訪問日誌等)命名方式:

appName_logType_logName.log。logType:日誌類型,推薦分類有stats/monitor/visit等;
logName:日誌描述。這種命名的好處:通過文件名就可知道日誌文件屬於什麼應用,什麼類型,什麼目的,也有利於歸類查找。 
正例:mppserver應用中單獨監控時區轉換異常,
如: mppserver_monitor_timeZoneConvert.log
說明:推薦對日誌進行分類,如將錯誤日誌和業務日誌分開存放,便於開發人員查看,也便於通過日誌對系統進行及時監控。

【強制】對trace/debug/info級別的日誌輸出,必須使用條件輸出形式或者使用佔位符的方式。

正例:(條件)
if (logger.isDebugEnabled()) {
logger.debug("Processing trade with id: " + id + " and symbol: " + symbol);
}
正例:(佔位符)
logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);

【強制】避免重複打印日誌,浪費磁盤空間,務必在log4j.xml中設置additivity=false。

正例:<logger name="com.taobao.dubbo.config" additivity="false">

【強制】異常信息應該包括兩類信息:案發現場信息和異常堆棧信息。如果不處理,那麼通過關鍵字throws往上拋出。

正例:logger.error(各類參數或者對象toString + "_" + e.getMessage(), e);

【推薦】謹慎地記錄日誌。生產環境禁止輸出debug日誌;有選擇地輸出info日誌;如果使用warn來記錄剛上線時的業務行爲信息,一定要注意日誌輸出量的問題,避免把服務器磁盤撐爆,並記得及時刪除這些觀察日誌。

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

【推薦】可以使用 warnwarn warn日誌級別來記錄用戶輸入參數錯誤的情況,避免投訴時無所適 從。如非必要,請不在此場景打出 errorerror errorerror級別,避免頻繁報警。 級

說明: 注意日誌輸出的級別, errorerror errorerror級別只記錄系統邏輯出錯、異常 級別只記錄系統邏輯出錯、異常 或者重要的錯誤信息。

其他

【強制】在使用正則表達式時,利用好其預編譯功能,可以有效加快正則匹配速度。

【強制】velocity調用POJO類的屬性時,建議直接使用屬性名取值即可,模板引擎會自動按規範調用POJO的getXxx(),如果是boolean基本數據類型變量(boolean命名不需要加is前綴),會自動調用isXxx()方法。

說明:注意如果是Boolean包裝類對象,優先調用getXxx()的方法。

【強制】注意 Math.random() 這個方法返回是double類型

注意取值的範圍 0≤x<1(能夠取到零值,注意除零異常),如果想獲取整數類型的隨機數,不要將x放大10的若干倍然後取整,直接使用Random對象的nextInt或者nextLong方法。

【強制】獲取當前毫秒數System.currentTimeMillis(); 而不是new Date().getTime();

說明:如果想獲取更加精確的納秒級時間值,使用System.nanoTime()的方式。在JDK8中,針對統計時間等場景,推薦使用Instant類。

【推薦】不要在視圖模板中加入任何複雜的邏輯。

說明:根據MVC理論,視圖的職責是展示,不要搶模型和控制器的活。

【推薦】任何數據結構的構造或初始化,都應指定大小,避免數據結構無限增長吃光內存。

【推薦】及時清理不再使用的代碼段或配置信息。

說明:對於垃圾代碼或過時配置,堅決清理乾淨,避免程序過度臃腫,代碼冗餘。
正例:對於暫時被註釋掉,後續可能恢復使用的代碼片斷,在註釋代碼上方,統一規定使用三個斜槓(///)來說明註釋掉代碼的理由。	

MySQL數據庫

建表規約

【強制】表達是與否概念的字段,必須使用is_xxx的方式命名,數據類型是unsigned tinyint

正例: 表達邏輯刪除的字段名 is_deleted,1表示刪除, 0表示未刪除。

【強制】表名、字段名必須使用小寫字母或數字,禁止出現數字開頭,禁止兩個下劃線中間只出現數字。數據庫字段名的修改代價很大,因爲無法進行預發佈,所以字段名稱需要慎重考慮。

說明: MySQLMySQLMySQLMySQL 在 WindowsWindows WindowsWindowsWindowsWindows下不區分大小寫,但在 下不區分大小寫,但在 LinuxLinux Linux下默認是區分大小寫。因此,數據庫 下默認是區分大小寫。因此,數據庫 下默認是區分大小寫。因此,數據庫 名、表字段,都不允許出現任何大寫母避免節外生枝。
正例:aliyun_admin,rdc_config,level3_name 
反例:AliyunAdmin,rdcConfig,level_3_name

【強制】表名不使用複數名詞。

說明:表名應該僅僅表示表裏面的實體內容,不應該表示實體數量,對應於DO類名也是單數形式,符合表達習慣。

【強制】禁用保留字,如desc、range、match、delayed等,請參考MySQL官方保留字。

【強制】主鍵索引名爲pk_字段名;唯一索引名爲uk_字段名;普通索引名則爲idx_字段名。

說明:pk_ 即primary key;uk_ 即 unique key;idx_ 即index的簡稱

【強制】小數類型爲decimal,禁止使用float和double。

說明:float和double在存儲的時候,存在精度損失的問題,很可能在值的比較時,得到不正確的結果。如果存儲的數據範圍超過decimal的範圍,建議將數據拆成整數和小數分開存儲。

【強制】如果存儲的字符串長度幾乎相等,使用char定長字符串類型。

【強制】varchar是可變長字符串,不預先分配存儲空間,長度不要超過5000,如果存儲長度大於此值,定義字段類型爲text,獨立出來一張表,用主鍵來對應,避免影響其它字段索引效率。

【強制】表必備三字段:id, gmt_create, gmt_modified。

說明: 其中 id必爲 主鍵,類型必爲 主鍵,類型unsigned bigint、單表時自增步長爲 、單表時自增步長爲 1。gmt_create, gmt_modified的類型均爲 的類型均爲 datetime類型,前者現 類型,前者現 在時表示主動創建,後者過去分詞被 動

【推薦】表的命名最好是加上“業務名稱_表的作用”。

正例:alipay_task / force_project / trade_config

【推薦】庫名與應用名稱儘量一致。

【推薦】如果修改字段含義或對字段表示的狀態追加時,需要及時更新字段註釋。

【推薦】字段允許適當冗餘,以提高查詢性能,但必須考慮數據一致。冗餘字段應遵循:

  • 不是頻繁修改的字段。
  • 不是varchar超長字段,更不能是text字段。

【推薦】單錶行數超過500萬行或者單表容量超過2GB,才推薦進行分庫分表。

說明:如果預計三年後的數據量根本達不到這個級別,請不要在創建表時就分庫分表。
https://blog.csdn.net/xlgen157387/article/details/53976153

【參考】合適的字符存儲長度,不但節約數據庫表空間、節約索引存儲,更重要的是提升檢索速度。

索引規約

【強制】業務上具有唯一特性的字段,即使是多個字段的組合,也必須建成唯一索引。

說明:不要以爲唯一索引影響了insert速度,這個速度損耗可以忽略,但提高查找速度是明顯的;另外,即使在應用層做了非常完善的校驗控制,只要沒有唯一索引,根據墨菲定律,必然有髒數據產生。

【強制】超過三個表禁止join。需要join的字段,數據類型必須絕對一致;多表關聯查詢時,保證被關聯的字段需要有索引。

【強制】在varchar字段上建立索引時,必須指定索引長度,沒必要對全字段建立索引,根據實際文本區分度決定索引長度即可。

【強制】頁面搜索嚴禁左模糊或者全模糊,如果需要請走搜索引擎來解決。

【推薦】如果有order by的場景,請注意利用索引的有序性。order by 最後的字段是組合索引的一部分,並且放在索引組合順序的最後,避免出現file_sort的情況,影響查詢性能.

【推薦】利用覆蓋索引來進行查詢操作,避免回表

說明:如果一本書需要知道第11章是什麼標題,會翻開第11章對應的那一頁嗎?目錄瀏覽一下就好,這個目錄就是起到覆蓋索引的作用。
正例:能夠建立索引的種類分爲主鍵索引、唯一索引、普通索引三種,而覆蓋索引只是一種查詢的一種效果,用explain的結果,extra列會出現:using index。

【推薦】利用延遲關聯或者子查詢優化超多分頁場景。

說明: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

【推薦】 SQL性能優化的目標:至少要達到 range 級別,要求是ref級別,如果可以是consts最好。

說明: 
1)consts 單表中最多隻有一個匹配行(主鍵或者唯一索引),在優化階段即可讀取到數據。 
2)ref 指的是使用普通的索引(normal index)。
3)range 對索引進行範圍檢索。 反例:explain表的結果,type=index,索引物理文件全掃描,速度非常慢,這個index級別比較range還低,與全表掃描是小巫見大巫。

【推薦】建組合索引的時候,區分度最高的在最左邊

正例:如果where a=? and b=? ,a列的幾乎接近於唯一值,那麼只需要單建idx_a索引即可。 說明:存在非等號和等號混合判斷條件時,在建索引時,請把等號條件的列前置。如:where a>? and b=? 那麼即使a的區分度更高,也必須把b放在索引的最前列。

【推薦】防止因字段類型不同造成的隱式轉換,導致索引失效。

【參考】創建索引時避免有如下極端誤解:

  • 寧濫勿缺。認爲一個查詢就需要建一個索引。
  • 寧缺勿濫。認爲索引會消耗空間、嚴重拖慢更新和新增速度。
  • 抵制惟一索引。認爲業務的惟一性一律需要在應用層通過“先查後插”方式解決。

SQL語句

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

說明:count(*)會統計值爲NULL的行,而count(列名)不會統計此列爲NULL值的行。

【強制】count(distinct col) 計算該列除NULL之外的不重複行數,注意 count(distinct col1, col2) 如果其中一列全爲NULL,那麼即使另一列有不同的值,也返回爲0。

【強制】當某一列的值全是NULL時,count(col)的返回結果爲0,但sum(col)的返回結果爲NULL,因此使用sum()時需注意NPE問題。

 正例:可以使用如下方式來避免sum的NPE問題:SELECT IF(ISNULL(SUM(g)),0,SUM(g)) FROM table;

【強制】使用ISNULL()來判斷是否爲NULL值。

說明:NULL與任何值的直接比較都爲NULL。 
NULL<>NULL的返回結果是NULL,而不是false。 
NULL=NULL的返回結果是NULL,而不是true。 
NULL<>1的返回結果是NULL,而不是true。

【強制】 在代碼中寫分頁查詢邏輯時,若count爲0應直接返回,避免執行後面的分頁語句。

【強制】不得使用外鍵與級聯,一切外鍵概念必須在應用層解決。

說明:以學生和成績的關係爲例,學生表中的student_id是主鍵,那麼成績表中的student_id則爲外鍵。如果更新學生表中的student_id,同時觸發成績表中的student_id更新,即爲級聯更新。外鍵與級聯更新適用於單機低併發,不適合分佈式、高併發集羣;級聯更新是強阻塞,存在數據庫更新風暴的風險;外鍵影響數據庫的插入速度。

【強制】禁止使用存儲過程,存儲過程難以調試和擴展,更沒有移植性。

【強制】數據訂正(特別是刪除、修改記錄操作)時,要先select,避免出現誤刪除,確認無誤才能執行更新語句。

【推薦】in操作能避免則避免,若實在避免不了,需要仔細評估in後邊的集合元素數量,控制在1000個之內。

【參考】如果有全球化需要,所有的字符存儲與表示,均以utf-8編碼,注意字符統計函數的區別。

【參考】 TRUNCATE TABLE 比 DELETE 速度快,且使用的系統和事務日誌資源少,但TRUNCATE無事務且不觸發trigger,有可能造成事故,故不建議在開發代碼中使用此語句。

說明:TRUNCATE TABLE 在功能上與不帶 WHERE 子句的 DELETE 語句相同。

ORM映射

####【強制】在表查詢中,一律不要使用 * 作爲查詢的字段列表,需要哪些字段必須明確寫明。 說明:1)增加查詢分析器解析成本。2)增減字段容易與resultMap配置不一致。

【強制】POJO類的布爾屬性不能加is,而數據庫字段必須加is_,要求在resultMap中進行字段與屬性之間的映射。

【強制】不要用resultClass當返回參數,即使所有類屬性名與數據庫字段一一對應,也需要定義;反過來,每一個表也必然有一個與之對應。 說明:配置映射關係,使字段與DO類解耦,方便維護。

【強制】sql.xml配置參數使用:#{},#param# 不要使用${} 此種方式容易出現SQL注入。

【強制】iBATIS自帶的queryForList(String statementName,int start,int size)不推薦使用。

【強制】不允許直接拿HashMap與Hashtable作爲查詢結果集的輸出。

說明:resultClass=”Hashtable”,會置入字段名和屬性值,但是值的類型不可控。

【強制】更新數據表記錄時,必須同時更新記錄對應的gmt_modified字段值爲當前時間。

【推薦】不要寫一個大而全的數據更新接口。傳入爲POJO類,不管是不是自己的目標更新字段,都進行update table set c1=value1,c2=value2,c3=value3; 這是不對的。執行SQL時,不要更新無改動的字段,一是易出錯;二是效率低;三是增加binlog存儲。

【參考】@Transactional事務不要濫用。事務會影響數據庫的QPS,另外使用事務的地方需要考慮各方面的回滾方案,包括緩存回滾、搜索引擎回滾、消息補償、統計修正等。

【參考】<isEqual>中的compareValue是與屬性值對比的常量,一般是數字,表示相等時帶上此條件;<isNotEmpty>表示不爲空且不爲null時執行;<isNotNull>表示不爲null值時執行。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章