常識以外的規範——阿里java開發手冊筆記(全章節)

說明

這篇文章是我第一次(認真)閱讀《阿里巴巴 Java 開發手冊(終極版)》的筆記。手冊自己對規範的講解已經很是詳細了,若是你已經有必定的開發經驗而且有良好的編碼習慣和意識,會發現大部分規範是符合常識的。因此本文不會再去作重複的說明,只是對其中一些可能沒留意到的或者說不在(個人)常識以內的一些規範進行整理記錄。固然每家公司都有本身的一套規範標準,因此你們也不必過度追究。html

其中或許會有遺漏或者理解錯誤,但願各位擔待提點。java

  1. 重點我會用黑體標註。
  2. 引用部分爲《阿里巴巴 Java 開發手冊(終極版)》原文
  3. 更新時間:2017-10-17

插件

ide插件已發佈:《阿里巴巴Java開發手冊》IDEA插件與Eclipse插件使用指南正則表達式


第一節 編程規約

1 命名規範

8.【強制】POJO 類中布爾類型的變量,都不要加 is,不然部分框架解析會引發序列化錯誤。spring

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

16.【參考】各層命名規約:
A) Service/DAO 層方法命名規約
1) 獲取單個對象的方法用 get 作前綴。
2) 獲取多個對象的方法用 list 作前綴。(我習慣寫成 getXxxList)
3) 獲取統計值的方法用 count 作前綴。
4) 插入的方法用 save/insert 作前綴。
5) 刪除的方法用 remove/delete 作前綴。
6) 修改的方法用 update 作前綴。編程

2 常量定義

1.【強制】不容許任何魔法值(即未經定義的常量)直接出如今代碼中。
反例:
String key = "Id#taobao_" + tradeId;
cache.put(key, value);api

魔法值:是指在代碼中直接出現的數值,而只有在這個數值記述的那部分代碼中才能明確瞭解其含義。
也就是咱們常說的[硬編碼]或者[寫死],這類代碼須要定義常量來明確其含義。數組

3 代碼格式

5.【強制】採用 4 個空格縮進,禁止使用 tab 字符。
說明:若是使用 tab 縮進,必須設置 1 個 tab 爲 4 個空格。IDEA 設置 tab 爲 4 個空格時,請勿勾選 Use tab character;而在 eclipse 中,必須勾選 insert spaces for tabs。安全

有些同窗可能會對這一條不覺得然。若是是協調開發,兩個工程師的格式化規則不一致極可能A同窗無心把B同窗的代碼從新格式化並提交,致使後邊查看svn變動記錄時傻逼了。服務器

7.【強制】單行字符數限制不超過 120 個,超出須要換行,換行時遵循以下原則:
1) 第二行相對第一行縮進 4 個空格,從第三行開始,再也不繼續縮進,參考示例。
2) 運算符與下文一塊兒換行。
3) 方法調用的點符號與下文一塊兒換行。
4) 方法調用時,多個參數,須要換行時,在逗號後進行。
5) 在括號前不要換行,見反例。

120這個長度限制頗有意思,如圖:
clipboard.png
這個長度大概是15寸筆記本1080分辨率字體14號左右的最佳可視長度。固然應該也不必定非要這麼精準吧。。

4 OOP 規約

7.【強制】全部的相同類型的包裝類對象之間值的比較,所有使用 equals 方法比較。
說明:對於 Integer var = ? 在-128 至 127 範圍內的賦值,Integer 對象是在IntegerCache.cache 產生,會複用已有對象,這個區間內的 Integer 值能夠直接使用==進行判斷,可是這個區間以外的全部數據,都會在堆上產生,並不會複用已有對象,這是一個大坑,推薦使用 equals 方法進行判斷。

12.【強制】POJO 類必須寫 toString 方法。使用 IDE 的中工具:source> generate toString時,若是繼承了另外一個 POJO 類,注意在前面加一下 super.toString。
說明:在方法執行拋出異常時,能夠直接調用 POJO 的 toString()方法打印其屬性值,便於排查問題。

吐槽:"使用 IDE 的中工具" 碼字錯誤哦!

13.【推薦】使用索引訪問用 String 的 split 方法獲得的數組時,需作最後一個分隔符後有無內容的檢查,不然會有拋 IndexOutOfBoundsException 的風險。
說明:
String str = "a,b,c,,";
String[] ary = str.split(",");
// 預期大於 3,結果是 3
System.out.println(ary.length);

最好的作法是對集合類型的變量自己進行判空校驗或者大小判斷,不要想固然。

5 集合處理

2.【強制】ArrayList的subList結果不可強轉成ArrayList,不然會拋出ClassCastException異常,即java.util.RandomAccessSubList cannot be cast to java.util.ArrayList.
說明:subList 返回的是 ArrayList 的內部類 SubList,並非 ArrayList ,而是ArrayList 的一個視圖,對於 SubList 子列表的全部操做最終會反映到原列表上。

5.【強制】使用工具類Arrays.asList()把數組轉換成集合時,不能使用其修改集合相關的方法,它的 add/remove/clear 方法會拋出 UnsupportedOperationException 異常。
說明:asList 的返回對象是一個 Arrays 內部類,並無實現集合的修改方法。Arrays.asList體現的是適配器模式,只是轉換接口,後臺的數據還是數組。
String[] str = new String[] { "you", "wu" };
List list = Arrays.asList(str);
第一種狀況:list.add("yangguanbao"); 運行時異常。
第二種狀況:str[0] = "gujin"; 那麼 list.get(0)也會隨之修改。

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 值組合集合。

java8 是個好東西~

6 併發處理

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 代替 SimpleDateFormat,官方給出的解釋:simple beautiful strong immutable thread-safe。

再說一遍,java8 是個好東西!LocalDateTime相關API
附贈一個java.util.Date和LocalDateTime互轉的例子:

private static Date localDateTimeToUDate(LocalDateTime localDateTime) {
       ZoneId zone = ZoneId.systemDefault();
       Instant instant = localDateTime.atZone(zone).toInstant();
       return Date.from(instant);
   }

   private static LocalDateTime uDateToLocalDate(Date date) {
       Instant instant = date.toInstant();
       ZoneId zone = ZoneId.systemDefault();
       LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
       return localDateTime;
   }

14.【參考】 HashMap 在容量不夠進行 resize 時因爲高併發可能出現死鏈,致使 CPU 飆升,在開發過程當中可使用其它數據結構或加鎖來規避此風險。

7 控制語句

3.【推薦】表達異常的分支時,少用 if-else 方式,這種方式能夠改寫成:
if (condition) {
...
return obj;
}
// 接着寫 else 的業務邏輯代碼;
說明:若是非得使用 if()...else if()...else...方式表達邏輯,【強制】避免後續代碼維護困難,請勿超過 3 層。
正例:超過 3 層的 if-else 的邏輯判斷代碼可使用衛語句、策略模式、狀態模式等來實現...

咱們公司codeReview時常常看到有些同窗的代碼是if(){}else if(){} else if(){}else{} 除了看上去low更主要的緣由是過多的大括號層級不便於閱讀很容易搞混,尤爲是跳出代碼塊的時候,連續幾個}}}基本就不知道跳到哪了完全懵逼,還得摺疊代碼或者滾上去從新回憶一下。

6.【推薦】接口入參保護,這種場景常見的是用於作批量操做的接口。

解釋一下,接口入參保護就是對入參進行校驗,包括容許的最大值或者其餘範圍或邊界。防止請求大量數據致使接口「爆炸」。好比限制返回數據最大條數,超過限制直接return或者拋異常。

8 註釋規約

感受沒啥好說的。。

9 其它

1.【強制】在使用正則表達式時,利用好其預編譯功能,能夠有效加快正則匹配速度。
說明:不要在方法體內定義:Pattern pattern = Pattern.compile(規則);

就是說定義成全局變量。


第二節 異常日誌

1 異常處理

3.【強制】對大段代碼進行try-catch,這是不負責任的表現。catch時請分清穩定代碼和非穩定代碼,穩定代碼指的是不管如何不會出錯的代碼。對於非穩定代碼的catch儘量進行區分異常類型,再作對應的異常處理。

9.【推薦】方法的返回值能夠爲null,不強制返回空集合,或者空對象等,必須添加註釋充分說明什麼狀況下會返回null值。調用方須要進行null判斷防止NPE問題。說明:本手冊明確防止NPE是調用者的責任。即便被調用方法返回空集合或者空對象,對調用者來講,也並不是高枕無憂,必須考慮到遠程調用失敗、序列化失敗、運行時異常等場景返回null的狀況。

須要說明的是是否能夠返回null是須要根據接口約定來判斷的,若是明確的返回對象的結構類型,必定要返回這個對象,但他的屬性值能夠是null,好比page對象:{data:null,pageNum:0,count:0}

2 日誌規約

4.【強制】對trace/debug/info級別的日誌輸出,必須使用條件輸出形式或者使用佔位符的方式。
說明:logger.debug("Processingtradewithid: " + id+ "andsymbol: " + symbol);若是日誌級別是warn,上述日誌不會打印,可是會執行字符串拼接操做,若是symbol是對象,會執行toString()方法,浪費了系統資源,執行了上述操做,最終日誌卻沒有打印。
正例:(條件)if (logger.isDebugEnabled()) { logger.debug("Processing trade with id: " + id + " and symbol: " + symbol); }
正例:(佔位符)logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);


第三節 單元測試

4.【強制】單元測試是能夠重複執行的,不能受到外界環境的影響。
說明:單元測試一般會被放到持續集成中,每次有代碼check in時單元測試都會被執行。若是單測對外部環境(網絡、服務、中間件等)有依賴,容易致使持續集成機制的不可用。
正例:爲了避免受外界環境影響,要求設計代碼時就把SUT的依賴改爲注入,在測試時用spring這樣的DI框架注入一個本地(內存)實現或者Mock實現。

15.【參考】爲了更方便地進行單元測試,業務代碼應避免如下狀況:
構造方法中作的事情過多。存在過多的全局變量和靜態方法。
存在過多的外部依賴。
存在過多的條件語句。說明:多層條件語句建議使用衛語句、策略模式、狀態模式等方式重構。

和第一節if-else提到的同樣,避免多層代碼塊嵌套

16.【參考】不要對單元測試存在以下誤解:
那是測試同窗乾的事情。本文是開發手冊,凡是本文內容都是與開發同窗強相關的。
單元測試代碼是多餘的。汽車的總體功能與各單元部件的測試正常與否是強相關的。
單元測試代碼不須要維護。一年半載後,那麼單元測試幾乎處於廢棄狀態。
單元測試與線上故障沒有辯證關係。好的單元測試可以最大限度地規避線上故障。

測試開發相親相愛是一家~


第四節 安全規約

4.【強制】用戶請求傳入的任何參數必須作有效性驗證
說明:忽略參數校驗可能致使:
pagesize過大致使內存溢出
惡意orderby致使數據庫慢查詢
任意重定向SQL注入
反序列化注入
正則輸入源串拒絕服務ReDoS
說明:Java代碼用正則來驗證客戶端的輸入,有些正則寫法驗證普通用戶輸入沒有問題,可是若是攻擊人員使用的是特殊構造的字符串來驗證,有可能致使死循環的結果。

老生常談的問題,但在工做中有時會忽略。


第五節 MySQL數據庫

1. 建表規約

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

大字段建外連表,避免影響索引效率。難點在於如何說服主工程師(滑稽)。

13.【推薦】字段容許適當冗餘,以提升查詢性能,但必須考慮數據一致。
冗餘字段應遵循:
1)不是頻繁修改的字段。
2)不是varchar超長字段,更不能是text字段。
正例:商品類目名稱使用頻率高,字段長度短,名稱基本一成不變,可在相關聯的表中冗餘存儲類目名稱,避免關聯查詢。

講一個笑話。公司老王出差去拉項目,對方博士生問「這個數據庫設計爲何不符合三範式?」
真事。

14.【推薦】單錶行數超過500萬行或者單表容量超過2GB,才推薦進行分庫分表。
說明:若是預計三年後的數據量根本達不到這個級別,請不要在建立表時就分庫分表。

2. 索引規約

3.【強制】在varchar字段上創建索引時,必須指定索引長度,不必對全字段創建索引,根據實際文本區分度決定索引長度便可。
說明:索引的長度與區分度是一對矛盾體,通常對字符串類型數據,長度爲20的索引,區分度會高達90%以上,可使用count(distinctleft(列名, 索引長度))/count(*)的區分度來肯定。

5.【推薦】若是有orderby的場景,請注意利用索引的有序性。orderby最後的字段是組合索引的一部分,而且放在索引組合順序的最後,避免出現file_sort的狀況,影響查詢性能。
正例:wherea=? andb=? orderbyc;索引:a_b_c
反例:索引中有範圍查找,那麼索引有序性沒法利用,如:WHEREa>10 ORDERBYb;索引a_b沒法排序。

6.【推薦】利用覆蓋索引來進行查詢操做,避免回表。
說明:若是一本書須要知道第11章是什麼標題,會翻開第11章對應的那一頁嗎?目錄瀏覽一下就好,這個目錄就是起到覆蓋索引的做用。
正例:可以創建索引的種類:主鍵索引、惟一索引、普通索引,而覆蓋索引是一種查詢的一種效果,用explain的結果,extra列會出現:usingindex。

3. SQL語句

1.【強制】不要使用count(列名)或count(常量)來替代count(*),count(*)是SQL92定義的標準統計行數的語法,跟數據庫無關,跟NULL和非NULL無關。
說明:count(*)會統計值爲NULL的行,而count(列名)不會統計此列爲NULL值的行。

乖乖滾回count(*)

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

4. ORM映射

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

編程一時爽,維護兩行淚~

5.【強制】iBATIS自帶的queryForList(StringstatementName,intstart,intsize)不推薦使用。
說明:其實現方式是在數據庫取到statementName對應的SQL語句的全部記錄,再經過subList取start,size的子集合
正例:
Map<String, Object> map = new HashMap<String, Object>();
map.put("start", start);
map.put("size", size);

沒想到你是這樣的iBATIS!


第六節 工程結構

1. 應用分層

2.【參考】(分層異常處理規約)在DAO層,產生的異常類型有不少,沒法用細粒度的異常進行catch,使用catch(Exceptione)方式,並thrownewDAOException(e),不須要打印日誌,由於日誌在Manager/Service層必定須要捕獲並打到日誌文件中去,若是同臺服務器再打日誌,浪費性能和存儲。在Service層出現異常時,必須記錄出錯日誌到磁盤,儘量帶上參數信息,至關於保護案發現場。若是Manager層與Service同機部署,日誌方式與DAO層處理一致,若是是單獨部署,則採用與Service一致的處理方式。Web層毫不應該繼續往上拋異常,由於已經處於頂層,若是意識到這個異常將致使頁面沒法正常渲染,那麼就應該直接跳轉到友好錯誤頁面,加上用戶容易理解的錯誤提示信息。開放接口層要將異常處理成錯誤碼和錯誤信息方式返回。

2. 二方庫依賴

10.【參考】爲避免應用二方庫的依賴衝突問題,二方庫發佈者應當遵循如下原則:
1)精簡可控原則。移除一切沒必要要的API和依賴,只包含ServiceAPI、必要的領域模型對象、Utils類、常量、枚舉等。若是依賴其它二方庫,儘可能是provided引入,讓二方庫使用者去依賴具體版本號;無log具體實現,只依賴日誌框架。
2)穩定可追溯原則。每一個版本的變化應該被記錄,二方庫由誰維護,源碼在哪裏,都須要能方便查到。除非用戶主動升級版本,不然公共二方庫的行爲不該該發生變化。

3. 服務器

4.【推薦】在線上生產環境,JVM的Xms和Xmx設置同樣大小的內存容量,避免在GC後調整堆大小帶來的壓力。

相關文章
相關標籤/搜索