1. 程式人生 > >阿裏巴巴 Java 代碼規範

阿裏巴巴 Java 代碼規範

使用 orm exist href tps 字符串類型 list 關系 target

1. 抽象類命名使用 Abstratc開頭。

2. 阿裏強制規定不允許任何魔法值(未經定義的常量)直接出現在代碼中。魔法值會讓代碼的可讀性大大降低,而且如果同樣的數值多次出現時,容易出現不清楚這些數值是否代表同樣的

含義。另一方面,如果本來應該使用相同的數值,一旦用錯,也難以發現。因此可以采用以下兩點,極力避免使用魔法數值。

3. 阿裏推薦如果變量值僅在一個範圍內變化,且帶有名稱之外的延伸屬性,定義為枚舉類。對於固定並且編譯時對象,如 Status、Type 等,應該采用 enum 而非自定義常量實現,enum 的

好處是類型更清楚,不會再編譯時混淆。這是一個建議性的試用推薦,枚舉可以讓開發者在 IDE 下使用更方便,也更安全。另外就是枚舉類型是一種具有特殊約束的類類型,這些約束的

存在使得枚舉類本身更加簡潔、安全、便捷。

4. 阿裏強制規定如果是大括號為空,則簡潔地寫成{}即可,不需要換行

5. 阿裏強制規定單行字符數限制不超過 120 個,超出需要換行,換行時遵循如下原則:

1).第二行相對第一行縮進 4 個空格,從第三行開始,不再繼續縮進,參考示例。

2).運算符與下文一起換行。

3).方法調用的點符號與下文一起換行。

4).方法調用時,多個參數,需要換行時,在逗號後進行。

5).在括號前不要換行

6. 阿裏強制規定代碼中避免通過一個類的對象引用訪問此類的靜態變量或靜態方法,暫時無謂增加編譯器解析成本,直接用類名來訪問即可。

為什麽需要這樣做呢?因為被 static 修飾過的變量或者方法都是隨著類的初始化產生的,在堆內存中有一塊專門的區域用來存放,後續直接用類名訪問即可,避免編譯成本的增加和實例

對象存放空間的浪費。

StackOverflow 上也有人提出了相同的疑問,網友較為精辟的回復是"這是由於生命周期決定的,靜態方法或者靜態變量不是以實例為基準的,而是以類為基準,所以直接用類訪問,否則

違背了設計初衷"。那為什麽還保留了實例的訪問方式呢?可能是因為允許應用方無汙染修改吧。

7. 阿裏強制規定相同參數類型、相同業務類型,才可以使用 Java 的可變參數,避免使用 Object,並且要求可變參數必須放置在參數列表的最後(提倡同學們盡量不用可變參數編程)。

擁有可變參數的方法可以被重載,在被調用時,如果能匹配到參數定長的方法則優先調用參數定長的方法。

8.單例模式:

1).雙檢查鎖機制(Double Check Locking),這裏在聲明變量時使用了 volatile 關鍵字來保證其線程間的可見性;在同步代碼塊中使用二次檢查,以保證其不被重復實例化。

集合其二者,這種實現方式既保證了其高效性,也保證了其線程安全性。

2).懶漢式:

9. 阿裏強制規定在一個 switch 塊內,每個 case 要麽通過 break/return 等來終止,要麽註釋說明程序將繼續執行到哪一個 case 為止;在一個 switch 塊內,都必須包含一個 default

語句並且放在最後,即使它什麽代碼也沒有。語法上來說,default 語句中的 break 是多余的,但是如果後續添加額外的 case,可以避免找不到匹配 case 項的錯誤。

10. 阿裏強制規定使用集合轉數組的方法,必須使用集合的 toArray(T[] arrays),傳入的是類型完全一樣的數組,大小就是 list.size()。使用 toArray 帶參方法,入參分配的數組空間不夠大時,

toArray 方法內部將重新分配內存空間,並返回新數組地址;如果數組元素大於實際所需,下標為[list.size()]的數組元素將被置為 null,其它數組元素保持原值,因此最好將方法入參數組大小定義與集合元素個數一致。

String[] array = new String[list.size()];

array = list.toArray(array);

11. 阿裏強制要求方法內部單行註釋,在被註釋語句上方另起一行,使用//註釋。方法內部多行註釋使用/**/註釋,註意與代碼對照。

百度規定方法註釋采用標準的 Javadoc 註釋規範,註釋中必須提供方法說明、參數說明及返回值和異常說明。騰訊規定采用 JavaDoc 文檔註釋,在方法定義之前應該對其進行註釋,

包括方法的描述、輸入、輸出以及返回值說明、拋出異常說明、參考鏈接等。

12. 不帶參數的 toArray()構造一個 Object 數組,然後進行數據拷貝

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

首先明確一點,阿裏這裏指的大小具體是指數據結構的最大長度。大部分 Java 集合類在構造時指定的大小都是初始尺寸(initial Capacity),而不是尺寸上限(Capacity),只有幾

種隊列除外,例如 ArrayBlockingQueue、LinkedBlockingQueue,它們在構造時可以指定隊列的最大長度。阿裏推薦的目的是為了合理規劃內存,避免出現 OOM(Out of Memory)異常。

14. 阿裏強制規定 Java 類庫中的 RuntimeException 可以通過預先檢查進行規避,而不應該通過 catch 來處理,例如 IndexOutOfBoundsException、NullPointerException 等。

RuntimeException,也被稱為運行時異常,通常是由於代碼中的 bug 引起的,正確的處理方式是去檢查代碼,通過添加數據長度判斷,判斷對象是否為空等方法區規避,而不是靠捕獲來規避這種異常。

15. 阿裏強制規定有 try 塊放到了事務代碼中,catch 異常後,如果需要回滾事務,一定要註意手動回滾事務。

try catch 代碼塊中對異常的處理,可能會遺漏事務的一致性,當事務控制不使用其他框架管理時,事務需要手動回滾。實際使用如果引入第三方的框架對事務進行管理,比如 Spring,則根據第三方

框架的實際實現情況,確定是否有必要手動回滾。當第三方事務管理框架本身就會對於異常進行拋出時需要做事務回滾。例如 Spring 在@Transactional 的 annotation 註解下,會默認開啟運行時異常事務回滾。

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

17. 阿裏強制規定日誌文件至少保存 15 天,因為有些異常具備以"周"為頻次發生的特點。

日誌保留時間推薦 15 天以上,但是保留時間也不宜過長,一般不超過 21 天,否則造成硬盤空間的浪費。對於一些長周期性執行的邏輯,可以根據實際情況調整該保存時間,同時也

需要保證日誌能夠監控到關鍵的應用。

對於長周期執行的邏輯,可以使用特定的 appender,並使用不同的日誌清理規則,如時間、大小等。如一月執行一次的定時任務,可以將日誌輸出到新的日誌文件,然後通過大小限定

的規則進行清理,並不一定要使用時間清理的邏輯。

18. 阿裏強制要求對於隸屬於用戶個人的頁面或者功能必須進行權限控制校驗。

涉及到對於數據的增刪改查,必須有權限的控制和校驗,要有一個黑白名單的控制,不能依賴於前臺頁面的簡單控制,後臺要有對於完整的權限控制的實現。這樣就能盡可能地防治數據的錯誤修改。

19. 阿裏強制要求用戶請求傳入的任何參數必須做有效校驗。

20.單元測試:

1). 單元測試應該自動執行:阿裏強制單元測試應該是全自動執行的,並且非交互式的。測試框架通常是定期執行的,執行過程必須完全自動化才有意義。輸出結果需要人工檢查的測試

不是一個號的單元測試。單元測試中不準使用 System.out 來進行人肉驗證,必須使用 assert 來驗證。

2). 單元測試應該是獨立的:阿裏強制保持單元測試的獨立性。為了保證單元測試穩定可靠且便於維護,單元測試用例之間決不能互相調用,也不能依賴執行的先後次序。反例:method2

需要依賴 method1 的執行,將執行結果作為 method2 的輸入。

編寫單元測試時遇到的這類依賴可以使用 mock 來模擬輸入和期望的返回,這樣所以來的方法內部邏輯的變更就不會影響到外部的實現。

3).阿裏推薦編寫單元測試代碼遵守 BCDE 原則,以保證被測試模塊的交付質量。

a). B(Border):確保參數邊界值均被覆蓋。例如:對於數字,測試負數、0、正數、最小值、最大值、NaN(非數字)、無窮大值等。對於字符串,測試空字符串、單字符、

非 ASCII 字符串、多字節字符串等。對於集合類型,測試空、第一個元素、最後一個元素等。對於日期,測試 1 月 1 日、2 月 29 日、12 月 31 日等。被測試的類本身

也會暗示一些特定情況下的邊界值。對於邊界情況的測試一定要詳盡。

b). C(Connect):確保輸入和輸出的正確關聯性。例如,測試某個時間判斷的方法 boolean inTimeZone(Long timeStamp),該方法根據輸入的時間戳判斷該事件是否存在於某個

時間段內,返回 boolean 類型。如果測試輸入的測試數據為 Long 類型的時間戳,對於輸出的判斷應該是對於 boolean 類型的處理。如果測試輸入的測試數據為非 Long 類型

數據,對於輸出的判斷應該是報錯信息是否正確。

c).D(Design):任務程序的開發包括單元測試都應該遵循設計文檔。

d).E(Error):單元測試包括對各種方法的異常測試,測試程序對異常的響應能力。

21.《單元測試之道(Java 版)》這本書裏面提到了關於邊界測試的 CORRECT 原則:

1).一致性(Conformance):值是否符合預期格式(正常的數據),列出所有可能不一致的數據,進行驗證。

2).有序性(Ordering):傳入的參數的順序不同的結果是否正確,對排序算法會產生影響,或者是對類的屬性賦值順序不同會不會產生錯誤。

3).區間性(Range):參數的取值範圍是否在某個合理的區間範圍內。

4).引用/耦合性(Reference):程序依賴外部的一些條件是否已滿足。前置條件:系統必須處於什麽狀態下,該方法才能運行。後置條件,你的方法將會保證哪些狀態發生改變。

5).存在性(Existence):參數是否真的存在,引用為 Null,String 為空,數值為 0 或者物理介質不存在時,程序是否能正常運行。

6).基數性(Cardinality):考慮以"0-1-N 原則",當數值分別為 0、1、N 時,可能出現的結果,其中 N 為最大值。

7).時間性(Time):相對時間指的是函數執行的依賴順序,絕對時間指的是超時問題、並發問題。

22. 阿裏強制要求如果遇到需要表達是與否的概念時,必須使用 is_xxx 的方法命令,數據類型是 unsigned tinyint,1 表示是,0 表示否。

說明:任務字段如果為非負數,必須是 unsigned。

命名使用 is_xxx 第一個好處是比較清晰的,第二個好處是使用者根據命名就可以知道這個字段的取值範圍,也方便做參數驗證。類型使用 unsigned 的好處是如果只存整數,

unsigned 類型有更大的取值範圍,可以節約磁盤和內存使用。

23. 對於表的名字,MySQL 社區有自己推薦的命名規範:

1).表包含多個英文單詞時,需要用下劃線進行單詞分割,這一點類似於 Java 類名的命名規範,例如 master_schedule、security_user_permission;

2).由於 InnoDB 存儲引擎本身是針對操作系統的可插拔設計的,所以原則上所有的表名組成全部由小寫字母組成;

3).不允許出現空格,需要分割一律采用下劃線;

4).名字不允許出現數字,僅包含英文字母;

5).名字需要總長度少於 64 個字符。

24. 數據類型精度考量:阿裏強制要求存放小數時使用 decimal,禁止使用 float 和 double。

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

實際上,所有涉及到數據存儲的類型定義,都會涉及數據精度損失問題。Java 的數據類型也存在 float 和 double 精度損失情況,阿裏沒有指出這條規約,就全文來說,這是一個比較嚴重的規約缺失。

25. 采用 BigDecimal 有一個缺點,就是使用過程中沒有原始數據這麽方便,效率也不高。如果采用 int 方式,最好不要在有小數點的場景下使用,可以在 100、10 這樣業務場景下選擇使用。

26. 阿裏強制要求如果存儲的字符串長度幾乎相等,使用 Char 定長字符串類型。

27. 服務間依賴關系:阿裏推薦默認上層依賴於下層,箭頭關系表示可直接依賴,如:開放接口層可以依賴於 Web 層,也可以直接依賴於 Service 層。

28. 阿裏推薦高並發服務器建議調小 TCP 協議的 time_wait 超時時間。

說明:操作系統默認 240 秒後才會關閉處於 time_wait 狀態的連接,在高並發訪問下,服務器端會因為處於 time_wait 的連接數太多,可能無法建立新的連接,所以需要在服務器上調小此等待值。

正例:在 Linux 服務器上通過變更/etc/sysctl.conf 文件去修改該缺省值(秒):net.ipv4.tcp_fin_timeout=30

參見:https://www.ibm.com/developerworks/cn/java/deconding-code-specification-part-1/index.html

https://www.ibm.com/developerworks/cn/java/deconding-code-specification-part-2/index.html?ca=drs-

阿裏巴巴 Java 代碼規範