阿里巴巴java開發手冊容易忽視的幾個知識點

俺就不起網名發表於2018-04-08

一、程式設計規約

(一)命名規範:
1、常量命名全部大寫,單詞間用下劃線隔開,力求語義表達完整清楚,不要嫌名字長,正確的如:MAX_STOCK_COUNT;
2、抽象類命名使用 Abstract 或 Base 開頭;異常類命名使用 Exception 結尾;
3、中括號是陣列型別的一部分,陣列定義如下:String[] args;
反例:使用String args[]的方式來定義。
3、POJO 類中布林型別的變數,都不要加 is,否則部分框架解析會引起序列化錯誤。
反例:定義為基本資料型別Boolean isDeleted;的屬性,它的方法也是isDeleted(),RPC框架在反向解析的時候,“以為”對應的屬性名稱是 deleted,導致屬性獲取不到,進而丟擲異
常。
4、如果使用到了設計模式,建議在類名中體現出具體模式
說明:將設計模式體現在名字中,有利於閱讀者快速理解架構設計思想。 
正例:public class OrderFactory;
public class LoginProxy; 
public class ResourceObserver;
5、介面類中的方法和屬性不要加任何修飾符號(public 也不要加),保持程式碼的簡潔性,並加上有效的 Javadoc 註釋。儘量不要在介面裡定義變數,如果一定要定義變數,肯定是與介面方法相關,並且是整個應用的基礎常量。
正例:介面方法簽名:void f();
介面基礎常量表示:String COMPANY = "alibaba";
6、列舉類名建議帶上 Enum 字尾,列舉成員名稱需要全大寫,單詞間用下劃線隔開。 
說明:列舉其實就是特殊的常量類,且構造方法被預設強制是私有。 
正例:列舉名字:DealStatusEnum,成員名稱:SUCCESS / UNKOWN_REASON。

(二)OOP規約

1、相同引數型別,相同業務含義,才可以使用 Java 的可變引數,避免使用 Object。 
說明:可變引數必須放置在引數列表的最後。(提倡同學們儘量不用可變引數程式設計) 
正例:public User getUsers(String type, Integer... ids) {...}
2、關於基本資料型別與包裝資料型別的使用標準如下:
1) 【強制】所有的POJO類屬性必須使用包裝資料型別。
2) 【強制】RPC方法的返回值和引數必須使用包裝資料型別。
3) 【推薦】所有的區域性變數使用基本資料型別。
3、構造方法裡面禁止加入任何業務邏輯,如果有初始化邏輯,請放在 init 方法中;
4、POJO 類必須寫 toString 方法。
使用 IDE 的中工具:source> generate toString 時,如果繼承了另一個 POJO 類,注意在前面加一下 super.toString。 說明:在方法執行丟擲異常時,可以直接呼叫 POJO 的 toString()方法列印其屬性值,便於排查問題。
5、final 可以宣告類、成員變數、方法、以及本地變數,下列情況使用 final 關鍵字: 
1) 不允許被繼承的類,如:String 類。
2) 不允許修改引用的域物件,如:POJO 類的域變數。
3) 不允許被重寫的方法,如:POJO 類的 setter 方法。
4) 不允許執行過程中重新賦值的區域性變數。
5) 避免上下文重複使用一個變數,使用 final 描述可以強制重新定義一個變數,方便更好 地進行重構。

(三)集合處理

1、關於 hashCode 和 equals 的處理,遵循如下規則:
1) 只要重寫equals,就必須重寫hashCode。
2) 因為Set儲存的是不重複的物件,依據hashCode和equals進行判斷,所以Set儲存的物件必須重寫這兩個方法。
3) 如果自定義物件做為Map的鍵,那麼必須重寫hashCode和equals。
說明:String 重寫了 hashCode 和 equals 方法,所以我們可以非常愉快地使用 String 物件 作為 key 來使用。
2、使用集合轉陣列的方法,必須使用集合的toArray(T[] array),傳入的是型別完全 一樣的陣列,大小就是 list.size()。
說明:使用 toArray 帶參方法,入參分配的陣列空間不夠大時,toArray 方法內部將重新分配記憶體空間,並返回新陣列地址;如果陣列元素大於實際所需,下標為[ list.size() ]的陣列元素將被置為 null,其它陣列元素保持原值,因此最好將方法入引數組大小定義與集合元素 個數一致。
正例:
List<String> list = new ArrayList<String>(2); list.add("guan");
list.add("bao");
String[] array = new String[list.size()]; array = list.toArray(array);
反例:直接使用 toArray 無參方法存在問題,此方法返回值只能是 Object[]類,若強轉其它 型別陣列將出現 ClassCastException 錯誤。

(四)併發處理

1、獲取單例物件需要保證執行緒安全,其中的方法也要保證執行緒安全。
說明:資源驅動類、工具類、單例工廠類都需要注意。
2、執行緒資源必須通過執行緒池提供,不允許在應用中自行顯式建立執行緒。 說明:使用執行緒池的好處是減少在建立和銷燬執行緒上所花的時間以及系統資源的開銷,解決資 源不足的問題。如果不使用執行緒池,有可能造成系統建立大量同類執行緒而導致消耗完記憶體或者 “過度切換”的問題。
3、執行緒池不允許使用 Executors 去建立,而是通過 ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同學更加明確執行緒池的執行規則,規避資源耗盡的風險。 說明:Executors返回的執行緒池物件的弊端如下:
1)FixedThreadPool 和 SingleThreadPool:
允許的請求佇列長度為 Integer.MAX_VALUE,可能會堆積大量的請求,從而導致 OOM。
2)CachedThreadPool 和 ScheduledThreadPool:
允許的建立執行緒數量為 Integer.MAX_VALUE,可能會建立大量的執行緒,從而導致 OOM。
4、併發修改同一記錄時,避免更新丟失,需要加鎖。要麼在應用層加鎖,要麼在快取加鎖,要麼在資料庫層使用樂觀鎖,使用 version 作為更新依據。 
說明:如果每次訪問衝突概率小於 20%,推薦使用樂觀鎖,否則使用悲觀鎖。樂觀鎖的重試次數不得小於 3 次。
5、ThreadLocal 無法解決共享物件的更新問題,ThreadLocal 物件建議使用 static
修飾。這個變數是針對一個執行緒內所有操作共有的,所以設定為靜態變數,所有此類例項共享此靜態變數 ,也就是說在類第一次被使用時裝載,只分配一塊儲存空間,所有此類的物件(只要是這個執行緒內定義的)都可以操控這個變數。


二、異常日誌

1、捕獲異常是為了處理它,不要捕獲了卻什麼都不處理而拋棄之,如果不想處理它,請將該異常拋給它的呼叫者。最外層的業務使用者,必須處理異常,將其轉化為使用者可以理解的內容。


三、資料庫

(一)建表規約

1、varchar 是可變長字串,不預先分配儲存空間,長度不要超過5000,如果儲存長度大於此值,定義欄位型別為text,獨立出來一張表,用主鍵來對應,避免影響其它欄位索引效率。
2、表的命名最好是加上“業務名稱_表的作用”。 正例:tiger_task / tiger_reader / mpp_config
3、欄位允許適當冗餘,以高查詢效能,但必須考慮資料一致。冗餘欄位應遵循:
1)不是頻繁修改的欄位。
2)不是 varchar 超長欄位,更不能是 text 欄位。
4、單錶行數超過 500 萬行或者單表容量超過 2GB,才推薦進行分庫分表。 說明:如果預計三年後的資料量根本達不到這個級別,請不要在建立表時就分庫分表。

(二)索引規約

1、超過三個表禁止 join。需要 join、 的欄位,資料型別必須絕對一致;多表關聯查詢 時,保證被關聯的欄位需要有索引。
2、頁面搜尋嚴禁左模糊或者全模糊,如果需要請走搜尋引擎來解決。
說明:索引檔案具有 B-Tree 的最左字首匹配特性,如果左邊的值未確定,那麼無法使用此索引。
3、如果有 order by 的場景,請注意利用索引的有序性。order by 最後的欄位是組合索引的一部分,並且放在索引組合順序的最後,避免出現 file_sort 的情況,影響查詢效能。 
正例:where a=? and b=? order by c; 索引:a_b_c 
反例:索引中有範圍查詢,那麼索引有序性無法利用,如:WHERE a>10 ORDER BY b; 索引 a_b 無法排序。
4、利用覆蓋索引來進行查詢操作,避免回表。
說明:如果一本書需要知道第 11 章是什麼標題,會翻開第 11 章對應的那一頁嗎?目錄瀏覽一下就好,這個目錄就是起到覆蓋索引的作用。 正例:能夠建立索引的種類:主鍵索引、唯一索引、普通索引,而覆蓋索引是一種查詢的一種 效果,用explain的結果,extra列會出現:using index。
5、建組合索引的時候,區分度最高的在最左邊。
正例:如果 where a=? and b=? ,a 列的幾乎接近於唯一值,那麼只需要單建 idx_a 索引即可。
說明:存在非等號和等號混合判斷條件時,在建索引時,請把等號條件的列前置。如:where a>? and b=? 那麼即使 a 的區分度更高,也必須把 b 放在索引的最前列。

(三)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;oracle使用nvl函式
4、在程式碼中寫分頁查詢邏輯時,若 count 為 0 應直接返回,避免執行後面的分頁語句。節省了查詢次數,提高了效能。
5、不得使用外來鍵與級聯,一切外來鍵概念必須在應用層解決。 
說明:(概念解釋)學生表中的 student_id 是主鍵,那麼成績表中的 student_id 則為外來鍵。 如果更新學生表中的 student_id,同時觸發成績表中的 student_id 更新,則為級聯更新。 外來鍵與級聯更新適用於單機低併發,不適合分散式、高併發叢集;級聯更新是強阻塞,存在數 據庫更新風暴的風險;外來鍵影響資料庫的插入速度。
6、資料訂正時,刪除和修改記錄時,要先 select然後加索檢查狀態,避免出現誤刪除或修改,確認無誤才能執行更新語句。
7、in 操作能避免則避免,若實在避免不了,需要仔細評估 in 後邊的集合元素數量,控制在 1000 個之內。超過1000個則建議使用分頁;
8、TRUNCATE TABLE 比 DELETE 速度快,且使用的系統和事務日誌資源少,但 TRUNCATE 無事務(不能回滾)且不觸發 trigger,有可能造成事故,故不建議在開發程式碼中使用此語句。

(四)ORM對映

1、POJO 類的布林屬性不能加 is,而資料庫欄位必須加 is_,要求在 resultMap 中進行 欄位與屬性之間的對映。
說明:參見定義 POJO 類以及資料庫欄位定義規定,在<resultMap>中增加對映,是必須的。 在 MyBatis Generator 生成的程式碼中,需要進行對應的修改。
2、sql.xml 配置引數使用:#{},#param# 不要使用${} 此種方式容易出現 SQL 注入。
3、不允許直接拿 HashMap 與 Hashtable 作為查詢結果集的輸出。
說明:resultClass=”Hashtable”,會置入欄位名和屬性值,但是值的型別不可控。
4、不要寫一個大而全的資料更新介面,傳入為 POJO 類,不管是不是自己的目標更新字
段,都進行 update table set c1=value1,c2=value2,c3=value3; 這是不對的。執行 SQL 時,不要更新無改動的欄位,一是易出錯;二是效率低;三是增加 binlog 儲存。

四、工程結構

1、DO(Data Object,也叫PO):與資料庫表結構一一對應,通過 DAO 層向上傳輸資料來源物件。
  DTO(Data Transfer Object):資料傳輸物件,Service 和 Manager 向外傳輸的物件。
  BO(Business Object):業務物件。可以由 Service 層輸出的封裝業務邏輯的物件。
  Query:資料查詢物件,各層接收上層的查詢請求。注:超過 2 個引數的查詢封裝,禁止 使用 Map 類來傳輸。
  VO(View Object):顯示層物件,通常是 Web 向模板渲染引擎層傳輸的物件。

相關文章