阿里巴巴Java開發手冊
不僅規範且獲益良多,真的是早該看一遍

阿里巴巴Java開發手冊

學sentinel時翻了一下alibaba的github,按照star排序第2名是個p3c,好奇點進去看原來是一份Java Coding Guidelines

個人知識重點

已經掌握的知識就不記了,以下都是我以前犯過的錯誤或是不懂原理的地方

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

  • 【強制】杜絕完全不規範的縮寫,避免望文不知義。 反例:AbstractClass“縮寫”命名成AbsClass;condition“縮寫”命名成 condi,此類隨意縮寫嚴重降低了代碼的可閲讀性。

  • 【強制】POJO類中布爾類型的變量,都不要加is前綴,否則部分框架解析會引起序列化錯誤。 反例:定義為基本數據類型Boolean isDeleted;的屬性,它的方法也是isDeleted(),RPC框架在反向解析的時候,“誤以為”對應的屬性名稱是deleted,導致屬性獲取不到,進而拋出異常。

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

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

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

  • 【強制】Object的equals方法容易拋空指針異常,應使用常量或確定有值的對象來調用equals。 正例:“test”.equals(object);

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

  • 關於基本數據類型與包裝數據類型的使用標準如下:

    • 【強制】所有的POJO類屬性必須使用包裝數據類型。
    • 【強制】RPC方法的返回值和參數必須使用包裝數據類型。
    • 【推薦】所有的局部變量使用基本數據類型。 説明:POJO類屬性沒有初值是提醒使用者在需要使用時,必須自己顯式地進行賦值,任何NPE問題,或者入庫檢查,都由使用者來保證。 正例:數據庫的查詢結果可能是null,因為自動拆箱,用基本數據類型接收有NPE風險。 反例:比如顯示成交總額漲跌情況,即正負x%,x為基本數據類型,調用的RPC服務,調用不成功時,返回的是預設值,頁面顯示為0%,這是不合理的,應該顯示成中劃線。所以包裝數據類型的null值,能夠表示額外的信息,如:遠程調用失敗,異常退出。
  • 【強制】定義DO/DTO/VO等POJO類時,不要設定任何屬性預設值。 反例:POJO類的gmtCreate預設值為new Date();但是這個屬性在數據提取時並沒有置入具體值,在更新其它字段時又附帶更新了此字段,導致創建時間被修改成當前時間。

  • 【推薦】避免採用取反邏輯運算符。 説明:取反邏輯不利於快速理解,並且取反邏輯寫法必然存在對應的正向邏輯寫法。

  • 【推薦】表達異常的分支時,少用if-else方式 説明:用if過濾即可,太多else會導致維護困難

  • 【強制】在高併發場景中,避免使用”等於”判斷作為中斷或退出的條件。 説明:如果併發控制沒有處理好,容易產生等值判斷被“擊穿”的情況,使用大於或小於的區間判斷條件來代替。

  • 反例:判斷剩餘獎品數量等於0時,終止發放獎品,但因為併發處理錯誤導致獎品數量瞬間變成了負數,GG

  • 【強制】類、類屬性、類方法的註釋必須使用Javadoc規範,使用/*內容/格式,不得使用// xxx方式。 説明:在IDE編輯窗口中,Javadoc方式會提示相關注釋,生成Javadoc可以正確輸出相應註釋;在IDE中,工程調用方法時,不進入方法即可懸浮提示方法、參數、返回值的意義,提高閲讀效率。

  • 【強制】方法內部單行註釋

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

  • 【推薦】與其“半吊子”英文來註釋,不如用中文註釋把問題説清楚。專有名詞與關鍵字保持英文原文即可。 反例:“TCP連接超時”解釋成“傳輸控制協議連接超時”,理解反而費腦筋。

  • 【參考】好的命名、代碼結構是自解釋的,註釋力求精簡準確、表達到位 put(elephant, fridge); 方法名put,加上兩個有意義的變量名elephant和fridge,已經説明了這是在幹什麼,語義清晰的代碼不需要額外的註釋。

  • 【強制】線程池不允許使用Executors去創建,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。 説明:Executors返回的線程池對象的弊端如下: 1)FixedThreadPool和SingleThreadPool: 允許的請求隊列長度為Integer.MAX_VALUE,可能會堆積大量的請求,從而導致OOM。 2)CachedThreadPool和ScheduledThreadPool: 允許的創建線程數量為Integer.MAX_VALUE,可能會創建大量的線程,從而導致OOM。

  • 【強制】高併發時,同步調用應該去考量鎖的性能損耗。能用無鎖數據結構,就不要用鎖;能鎖區塊,就不要鎖整個方法體;能用對象鎖,就不要用類鎖。 説明:儘可能使加鎖的代碼塊工作量儘可能的小,避免在鎖代碼塊中調用RPC方法。

  • 【強制】異常不要用來做流程控制,條件控制。 説明:異常設計的初衷是解決程序運行中的各種意外情況,且異常的處理效率比條件判斷方式要低很多。

  • 【強制】catch時請分清穩定代碼和非穩定代碼,穩定代碼指的是無論如何不會出錯的代碼。對於非穩定代碼的catch儘可能進行區分異常類型,再做對應的異常處理。 説明:對大段代碼進行try-catch,使程序無法根據不同的異常做出正確的應激反應,也不利於定位問題,這是一種不負責任的表現。 正例:用户註冊的場景中,如果用户輸入非法字符,或用户名稱已存在,或用户輸入密碼過於簡單,在程序上作出分門別類的判斷,並提示給用户。

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

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

  • 【強制】finally塊必須對資源對象、流對象進行關閉,有異常也要做try-catch。 説明:如果JDK7及以上,可以使用try-with-resources方式。

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

  • 【強制】獲取當前毫秒數用System.currentTimeMillis();


上次修改於 2022-02-07