阿里巴巴Java開發手冊閱讀筆記

qqxx6661發表於2019-02-07

前言

參考:阿里巴巴Java開發手冊V1.3.0

總結比較重要的,對面試有用的開發規約

一、程式設計規約

(一)命名風格

  • 【強制】POJO 類中布林型別的變數,都不要加 is,否則部分框架解析會引起序列化錯誤。

反例:定義為基本資料型別 Boolean isDeleted;的屬性,它的方法也是 isDeleted(),RPC 框架在反向解析的時候,“以為”對應的屬性名稱是 deleted,導致屬性獲取不到,進而丟擲異常。

  • 【推薦】如果模組、介面、類、方法使用了設計模式,在命名時體現出具體模式。

public class OrderFactory;

public class LoginProxy;

public class ResourceObserver;

(二)常量定義

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

反例:String key = "Id#taobao_" + tradeId;

cache.put(key, value);

  • 【推薦】如果變數值僅在一個範圍內變化,且帶有名稱之外的延伸屬性,定義為列舉類。

正例:public Enum { MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4), FRIDAY(5), SATURDAY(6), SUNDAY(7);}

(三)程式碼格式

(四)OOP 規約

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

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

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

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

  • 【強制】序列化類新增屬性時,請不要修改 serialVersionUID 欄位,避免反序列失敗;如果完全不相容升級,避免反序列化混亂,那麼請修改 serialVersionUID 值。

說明:注意 serialVersionUID 不一致會丟擲序列化執行時異常。

  • 【強制】構造方法裡面禁止加入任何業務邏輯,如果有初始化邏輯,請放在 init 方法中。

  • 【強制】POJO 類必須寫 toString 方法。使用 IDE 的中工具:source> generate toString 時,如果繼承了另一個 POJO 類,注意在前面加一下 super.toString。

說明:在方法執行丟擲異常時,可以直接呼叫 POJO 的 toString()方法列印其屬性值,便於排 查問題。

  • 【推薦】迴圈體內,字串的連線方式,使用 StringBuilder 的 append 方法進行擴充套件。

說明:反編譯出的位元組碼檔案顯示每次迴圈都會 new 出一個 StringBuilder 物件,然後進行 append 操作,最後通過 toString 方法返回 String 物件,造成記憶體資源浪費。

反例:

String str = "start";
for (int i = 0; i < 100; i++) {
str = str + "hello";
}
複製程式碼

(五)集合處理

  • 【強制】關於 hashCode 和 equals 的處理,遵循如下規則:
    • 1) 只要重寫 equals,就必須重寫 hashCode。
    • 2) 因為 Set 儲存的是不重複的物件,依據 hashCode 和 equals 進行判斷,所以 Set 儲存的物件必須重寫這兩個方法。
    • 3) 如果自定義物件做為 Map 的鍵,那麼必須重寫 hashCode 和 equals。

說明:String 重寫了 hashCode 和 equals 方法,所以我們可以非常愉快地使用 String 物件 作為 key 來使用。

  • 【強制】ArrayList的subList結果不可強轉成ArrayList,否則會丟擲ClassCastException 異常,即 java.util.RandomAccessSubList cannot be cast to java.util.ArrayList.

說明:subList 返回的是 ArrayList 的內部類 SubList,並不是 ArrayList ,而是 ArrayList 的一個檢視,對於 SubList 子列表的所有操作最終會反映到原列表上。

  • 【強制】使用集合轉陣列的方法,必須使用集合的 toArray(T[] array),傳入的是型別完全一樣的陣列,大小就是 list.size()。

反例:直接使用 toArray 無參方法存在問題,此方法返回值只能是 Object[]類,若強轉其它 型別陣列將出現 ClassCastException 錯誤。

  • 【強制】使用工具類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)也會隨之修改。

  • 【強制】不要在 foreach 迴圈裡進行元素的 remove/add 操作。remove 元素請使用 Iterator方式,如果併發操作,需要對 Iterator 物件加鎖。

  • 【推薦】高度注意 Map 類集合 K/V 能不能儲存 null 值的情況,如下表格:

在這裡插入圖片描述

(六)併發處理

  • 【強制】執行緒資源必須通過執行緒池提供,不允許在應用中自行顯式建立執行緒。

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

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

  • 【推薦】避免 Random 例項被多執行緒使用,雖然共享該例項是執行緒安全的,但會因競爭同一seed 導致的效能下降。

說明:Random 例項包括 java.util.Random 的例項或者 Math.random()的方式。

正例:在 JDK7 之後,可以直接使用 API ThreadLocalRandom,而在 JDK7 之前,需要編碼保 證每個執行緒持有一個例項。

  • 【參考】volatile 解決多執行緒記憶體不可見問題。對於一寫多讀,是可以解決變數同步問題,但是如果多寫,同樣無法解決執行緒安全問題。如果是count++操作,使用如下類實現:AtomicInteger count = new AtomicInteger(); count.addAndGet(1); 如果是 JDK8,推薦使用 LongAdder 物件,比 AtomicLong 效能更好(減少樂觀鎖的重試次數)。

(七)控制語句

  • 【推薦】表達異常的分支時,少用 if-else 方式

說明:如果非得使用 if()...else if()...else...方式表達邏輯,【強制】避免後續程式碼維 護困難,請勿超過 3 層。 正例:超過 3 層的 if-else 的邏輯判斷程式碼可以使用衛語句、策略模式、狀態模式等來實現, 其中衛語句示例如下:

public void today() {
 if (isBusy()) {
System.out.println(“change time.”);
 return;
 }
 if (isFree()) {
System.out.println(“go to travel.”);
 return;
 }
 System.out.println(“stay at home to learn Alibaba Java Coding Guidelines.”);
 return;
}
複製程式碼

(八)註釋規約

(九)其它

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

  • 【強制】後臺輸送給頁面的變數必須加$!{var}——中間的感嘆號。

說明:如果 var=null 或者不存在,那麼${var}會直接顯示在頁面上。

  • 【強制】注意 Math.random() 這個方法返回是 double 型別,注意取值的範圍 0≤x<1(能夠 取到零值,注意除零異常),如果想獲取整數型別的隨機數,不要將 x 放大 10 的若干倍然後 取整,直接使用 Random 物件的 nextInt 或者 nextLong 方法。

二、異常日誌

(一)異常處理

  • 【強制】Java 類庫中定義的一類 RuntimeException 可以通過預先檢查進行規避,而不應該 通過 catch 來處理,比如:IndexOutOfBoundsException,NullPointerException 等等。

正例:if (obj != null) {...}

反例:try { obj.method() } catch (NullPointerException e) {...}

  • 【強制】異常不要用來做流程控制,條件控制,因為異常的處理效率比條件分支低。

  • 【強制】對大段程式碼進行 try-catch,這是不負責任的表現

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

  • 【推薦】防止 NPE,是程式設計師的基本修養.

  • 【參考】在程式碼中使用“拋異常”還是“返回錯誤碼”,對於公司外的 http/api 開放介面必須 使用“錯誤碼”;而應用內部推薦異常丟擲;跨應用間 RPC 呼叫優先考慮使用 Result 方式,封 裝 isSuccess()方法、“錯誤碼”、“錯誤簡簡訊息”

(二)日誌規約

  • 【強制】日誌檔案推薦至少儲存 15 天,因為有些異常具備以“周”為頻次發生的特點。

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

三、單元測試

  • 【強制】好的單元測試必須遵守 AIR 原則。 說明:單元測試線上上執行時,感覺像空氣(AIR)一樣並不存在,但在測試質量的保障上, 卻是非常關鍵的。好的單元測試巨集觀上來說,具有自動化、獨立性、可重複執行的特點。
    • A:Automatic(自動化)
    • I:Independent(獨立性)
    • R:Repeatable(可重複)

四、安全規約

  • 【強制】隸屬於使用者個人的頁面或者功能必須進行許可權控制校驗。

  • 【強制】使用者輸入的 SQL 引數嚴格使用引數繫結或者 METADATA 欄位值限定,防止 SQL 注入, 禁止字串拼接 SQL 訪問資料庫。

  • 【強制】使用者請求傳入的任何引數必須做有效性驗證。

五、MySQL 資料庫

(一) 建表規約

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

  • 【強制】表達是與否概念的欄位,必須使用 is _ xxx 的方式命名,資料型別是 unsigned tinyint( 1 表示是,0 表示否 )

    • 說明:任何欄位如果為非負數,必須是 unsigned 。
    • 正例:表達邏輯刪除的欄位名 is_deleted ,1 表示刪除,0 表示未刪除。
  • 【強制】 varchar 是可變長字串,不預先分配儲存空間,長度不要超過 5000,如果儲存長 度大於此值,定義欄位型別為 text ,獨立出來一張表,用主鍵來對應,避免影響其它欄位索 引效率。

  • 【強制】表必備三欄位: id , gmt _ create , gmt _ modified 。

    • 說明:其中 id 必為主鍵,型別為 unsigned bigint 、單表時自增、步長為 1。 gmt_create, gmt_modified 的型別均為 date_time 型別,前者現在時表示主動建立,後者過去分詞表示被 動更新。
  • 【推薦】欄位允許適當冗餘,以提高查詢效能,但必須考慮資料一致。冗餘欄位應遵循:

    • 1 ) 不是頻繁修改的欄位。
    • 2 ) 不是 varchar 超長欄位,更不能是 text 欄位。
    • 正例:商品類目名稱使用頻率高,欄位長度短,名稱基本一成不變,可在相關聯的表中冗餘存 儲類目名稱,避免關聯查詢。

(二) 索引規約

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

  • 【強制】超過三個表禁止 join。需要 join 的欄位,資料型別必須絕對一致;多表關聯查詢時, 保證被關聯的欄位需要有索引。

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

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

  • 【推薦】利用覆蓋索引來進行查詢操作,避免回表。說明:如果一本書需要知道第 11 章是什麼標題,會翻開第 11 章對應的那一頁嗎?目錄瀏覽 一下就好,這個目錄就是起到覆蓋索引的作用。

正例:能夠建立索引的種類:主鍵索引、唯一索引、普通索引,而覆蓋索引是一種查詢的一種 效果,用 explain 的結果,extra 列會出現:using index。

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

正例:如果 where a=? and b=? ,a 列的幾乎接近於唯一值,那麼只需要單建 idx_a 索引即 可。

(三) SQL語句

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

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

    • 說明:以學生和成績的關係為例,學生表中的 student _ id 是主鍵,那麼成績表中的 student _ id則為外來鍵。如果更新學生表中的 student _ id ,同時觸發成績表中的 student _ id 更新,即為級聯更新。外來鍵與級聯更新適用於單機低併發,不適合分散式、高併發叢集 ; 級聯更新是強阻塞,存在資料庫更新風暴的風險 ; 外來鍵影響資料庫的插入速度。
  • 【強制】禁止使用儲存過程,儲存過程難以除錯和擴充套件,更沒有移植性。

(四)ORM 對映

  • 【強制】在表查詢中,一律不要使用 * 作為查詢的欄位列表,需要哪些欄位必須明確寫明。

六、工程結構

(一)應用分層

在這裡插入圖片描述

(二)二方庫依賴

(三)伺服器

  • 【推薦】高併發伺服器建議調小 TCP 協議的 time_ wait 超時時間。

    • 說明:作業系統預設 240 秒後,才會關閉處於 time_ wait 狀態的連線,在高併發訪問下,伺服器端會因為處於 time _ wait的連線數太多,可能無法建立新的連線,所以需要在伺服器上調小此等待值。
    • 正例:在 linux 伺服器上請通過變更/ etc / sysctl . conf 檔案去修改該預設值 ( 秒 ) : net . ipv 4. tcp _ fin _ timeout = 30
  • 【推薦】調大伺服器所支援的最大檔案控制程式碼數 (File Descriptor ,簡寫為 fd) 。

    • 說明:主流作業系統的設計是將 TCP / UDP 連線採用與檔案一樣的方式去管理,即一個連線對 應於一個 fd 。主流的 linux 伺服器預設所支援最大 fd 數量為 1024,當併發連線數很大時很 容易因為 fd 不足而出現“ open too many files ”錯誤,導致新的連線無法建立。 建議將 linux 伺服器所支援的最大控制程式碼數調高數倍 ( 與伺服器的記憶體數量相關 ) 。
  • 【推薦】給 JVM 設定-XX:+HeapDumpOnOutOfMemoryError 引數,讓 JVM 碰到 OOM 場景時輸出 dump 資訊。

  • 【推薦】線上上生產環境, JVM 的 Xms 和 Xmx 設定一樣大小的記憶體容量,避免在 GC 後調整堆 大小帶來的壓力。

  • 【參考】伺服器內部重定向使用 forward; 外部重定向地址使用 URL 拼裝工具類來生成,否則 會帶來 URL 維護不一致的問題和潛在的安全風險。

關注我

我是蠻三刀把刀,目前為後臺開發工程師。主要關注後臺開發,網路安全,Python爬蟲等技術。

來微信和我聊聊:yangzd1102

Github:github.com/qqxx6661

原創部落格主要內容

  • 筆試面試複習知識點手冊
  • Leetcode演算法題解析(前150題)
  • 劍指offer演算法題解析
  • Python爬蟲相關技術分析和實戰
  • 後臺開發相關技術分析和實戰

個人公眾號:Rude3Knife

個人公眾號:Rude3Knife

如果文章對你有幫助,不妨收藏起來並轉發給您的朋友們~

相關文章