唯品會Java開發手冊

《唯品會Java開發手冊》1.0.2版

  1. 概述
    《阿里巴巴Java開發手冊》,是首個對外公佈的企業級Java開發手冊,對整個業界都有重要的意義。
    我們結合唯品會的內部經驗,參考《Clean Code》、《Effective Java》等重磅資料,增補了一些條目,也做了些精
    簡。
    感謝阿里授權我們定製和再發布。
  2. 規範正文
  3. 命名規約
  4. 格式規約
  5. 註釋規約
  6. 方法設計
  7. 類設計
  8. 控制語句
  9. 基本類型
  10. 集合處理
  11. 併發處理
  12. 異常處理
  13. 日誌規約
  14. 其他設計
  15. 阿里手冊的增補與刪減記錄
    注意: 如需全文pdf版,請下載源碼,在docs/standard/目錄運行merge.sh生成,閱讀時的章節跳轉使用pdf閱讀器的
    左側書籤。
  16. 規範落地
    規則落地主要依靠代碼格式化模版與Sonar代碼規則檢查。
    其中Sonar規則不盡如人意的地方,我們進行了定製。
    Eclipse/Intellij 格式模板
    Sonar 規則修改示例
  17. 參考資料
    學習交流或獲取更多資料歡迎加入QQ羣:874514813《Clean Code》
    《Effective Java 2nd》
    《SEI CERT Oracle Coding Standard for Java》(在線版)
    Sonar代碼檢查規則
  18. 定製記錄
    《唯品會Java開發手冊》-與阿里手冊的比較文學I
    (一) 命名規約
    Rule 1. 【強制】禁止拼音縮寫,避免閱讀者費勁猜測;儘量不用拼音,除非中國式業務詞彙沒有通用易懂的英文對
    應。
    Rule 2. 【強制】禁止使用非標準的英文縮寫
    Rule 3. 【強制】禁用其他編程語言風格的前綴和後綴
    在其它編程語言中使用的特殊前綴或後綴,如_name, name_, mName, i_name,在Java中都不建議使用。
    Rule 4. 【推薦】命名的好壞,在於其“模糊度”
    1)如果上下文很清晰,局部變量可以使用 list 這種簡略命名, 否則應該使用 userList 這種更清晰的命名。
    2)禁止 a1, a2, a3 這種帶編號的沒誠意的命名方式。
    3)方法的參數名叫 bookList ,方法裏的局部變量名叫 theBookList 也是很沒誠意。
    4)如果一個應用裏同時存在 Account、AccountInfo、AccountData 類,或者一個類裏同時有 getAccountInfo()、
    getAccountData(), save()、 store() 的函數,閱讀者將非常困惑。
    5) callerId 與 calleeId, mydearfriendswitha 與 mydearfriendswithb 這種拼寫極度接近,考驗閱讀者眼力的。
    禁止: DZ[打折] / getPFByName() [評分]
    儘量避免:Dazhe / DaZhePrice
    反例: AbstractClass 縮寫成 AbsClass;condition 縮寫成 condi。Rule 5. 【推薦】包名全部小寫。點分隔符之間儘量只有一個英語單詞,即使有多個單詞也不使用下劃線或大小寫分

    Sonar-120:Package names should comply with a naming convention
    Rule 6. 【強制】類名與接口名使用UpperCamelCase風格,遵從駝峯形式
    Tcp, Xml等縮寫也遵循駝峯形式,可約定例外如:DTO/ VO等。
    Sonar-101:Class names should comply with a naming convention
    Sonar-114:Interface names should comply with a naming convention
    Rule 7. 【強制】方法名、參數名、成員變量、局部變量使用lowerCamelCase風格,遵從駝峯形式
    Sonar-100:Method names should comply with a naming convention
    Sonar-116:Field names should comply with a naming convention
    Sonar-117:Local variable and method parameter names should comply with a naming convention
    Rule 8. 【強制】常量命名全大寫,單詞間用下劃線隔開。力求語義表達完整清楚,不要嫌名字長
    例外:當一個static final字段不是一個真正常量,比如不是基本類型時,不需要使用大寫命名。
    例外:枚舉常量推薦全大寫,但如果歷史原因未遵循也是允許的,所以我們修改了Sonar的規則。
    Sonar-115:Constant names should comply with a naming convention
    Sonar-308:Static non-final field names should comply with a naming convention
    正例: com.vip.javatool
    反例: com.vip.java_tool, com.vip.javaTool
    正例:UserId / XmlService / TcpUdpDeal / UserVO
    反例:UserID / XMLService / TCPUDPDeal / UserVo
    正例: localValue / getHttpMessage();
    正例: MAX_STOCK_COUNT
    反例: MAX_COUNT
    private static final Logger logger = Logger.getLogger(MyClass.class);
    學習交流或獲取更多資料歡迎加入QQ羣:874514813Rule 9. 【推薦】如果使用到了通用的設計模式,在類名中體現,有利於閱讀者快速理解設計思想
    Rule 10. 【推薦】枚舉類名以Enum結尾; 抽象類使用Abstract或Base開頭;異常類使用Exception結尾;測試類以它
    要測試的類名開始,以Test結尾
    Sonar-2166:Classes named like “Exception” should extend “Exception” or a subclass
    Sonar-3577:Test classes should comply with a naming convention
    Rule 11. 【推薦】實現類儘量用Impl的後綴與接口關聯,除了形容能力的接口
    Rule 12. 【強制】POJO類中布爾類型的變量名,不要加is前綴,否則部分框架解析會引起序列化錯誤
    Rule 13. 【強制】避免成員變量,方法參數,局部變量的重名複寫,引起混淆
    類的私有成員變量名,不與父類的成員變量重名
    方法的參數名/局部變量名,不與類的成員變量重名 (getter/setter例外)
    下面錯誤的地方,Java在編譯時很坑人的都是合法的,但給閱讀者帶來極大的障礙。
    正例:OrderFactory, LoginProxy ,ResourceObserver
    正例:DealStatusEnum, AbstractView,BaseView, TimeoutException,UserServiceTest
    正例:CacheServiceImpl 實現 CacheService接口。
    正例: Foo 實現 Translatable接口。
    反例:Boolean isSuccess的成員變量,它的GET方法也是isSuccess(),部分框架在反射解析的時候,“以爲”對應的
    成員變量名稱是success,導致出錯。
    public class A {
    int foo;
    } p
    ublic class B extends A {
    int foo; //WRONG
    int bar;
    public void hello(int bar) { //WRONG
    int foo = 0; //WRONG
    } p
    ublic void setBar(int bar) { //OK
    this.bar = bar;Sonar-2387: Child class fields should not shadow parent class fields
    Sonar: Local variables should not shadow class fields
    (二) 格式規約
    Rule 1. 【強制】使用項目組統一的代碼格式模板,基於IDE自動的格式化
    1)IDE的默認代碼格式模板,能簡化絕大部分關於格式規範(如空格,括號)的描述。
    2)統一的模板,並在接手舊項目先進行一次全面格式化,可以避免, 不同開發者之間,因爲格式不統一產生代碼
    合併衝突,或者代碼變更日誌中因爲格式不同引起的變更,掩蓋了真正的邏輯變更。
    3)設定項目組統一的行寬,建議120。
    4)設定項目組統一的縮進方式(Tab或二空格,四空格均可),基於IDE自動轉換。
    VIP代碼格式化模板
    Rule 2. 【強制】IDE的text file encoding設置爲UTF-8; IDE中文件的換行符使用Unix格式,不要使用Windows格式
    Rule 3. 【推薦】 用小括號來限定運算優先級
    我們沒有理由假設讀者能記住整個Java運算符優先級表。除非作者和Reviewer都認爲去掉小括號也不會使代碼被誤
    解,甚至更易於閱讀。
    Sonar-1068:Limited dependence should be placed on operator precedence rules in expressions,我們修改了三目運算符
    foo!=null:foo:"" 不需要加括號。
    Rule 4. 【推薦】類內方法定義的順序,不要“總是在類的最後添加新方法”
    一個類就是一篇文章,想象一個閱讀者的存在,合理安排方法的佈局。
    1)順序依次是:構造函數 > (公有方法>保護方法>私有方法) > getter/setter方法。
    如果公有方法可以分成幾組,私有方法也緊跟公有方法的分組。
    2)當一個類有多個構造方法,或者多個同名的重載方法,這些方法應該放置在一起。其中參數較多的方法在後
    面。
    }
    } i
    f ((a == b) && (c == d))
    學習交流或獲取更多資料歡迎加入QQ羣:8745148133)作爲調用者的方法,儘量放在被調用的方法前面。
    Rule 5. 【推薦】通過空行進行邏輯分段
    一段代碼也是一段文章,需要合理的分段而不是一口氣讀到尾。
    不同組的變量之間,不同業務邏輯的代碼行之間,插入一個空行,起邏輯分段的作用。
    而聯繫緊密的變量之間、語句之間,則儘量不要插入空行。
    Rule 6.【推薦】避免IDE格式化
    對於一些特殊場景(如使用大量的字符串拼接成一段文字,或者想把大量的枚舉值排成一列),爲了避免IDE自動
    格式化,土辦法是把註釋符號//加在每一行的末尾,但這有視覺的干擾,可以使用@formatter:off和@formatter:on來
    包裝這段代碼,讓IDE跳過它。
    (三) 註釋規約
    Rule 1.【推薦】基本的註釋要求
    public Foo(int a) {…}
    public Foo(int a, String b) {…}
    public void foo(int a) {…}
    public void foo(int a, String b) {…}
    public void foo() {
    bar();
    } p
    ublic void bar() {…}
    int width;
    int height;
    String name;
    // @formatter:off

    // @formatter:on完全沒有註釋的大段代碼對於閱讀者形同天書,註釋是給自己看的,即使隔很長時間,也能清晰理解當時的思路;
    註釋也是給繼任者看的,使其能夠快速接替自己的工作。
    代碼將被大量後續維護,註釋如果對閱讀者有幫助,不要吝嗇在註釋上花費的時間。(但也綜合參見規則2,3)
    第一、能夠準確反應設計思想和代碼邏輯;第二、能夠描述業務含義,使別的程序員能夠迅速瞭解到代碼背後的信
    息。
    除了特別清晰的類,都儘量編寫類級別註釋,說明類的目的和使用方法。
    除了特別清晰的方法,對外提供的公有方法,抽象類的方法,同樣儘量清晰的描述:期待的輸入,對應的輸出,錯
    誤的處理和返回碼,以及可能拋出的異常。
    Rule 2. 【推薦】通過更清晰的代碼來避免註釋
    在編寫註釋前,考慮是否可以通過更好的命名,更清晰的代碼結構,更好的函數和變量的抽取,讓代碼不言自明,
    此時不需要額外的註釋。
    Rule 3. 【推薦】刪除空註釋,無意義註釋
    《Clean Code》建議,如果沒有想說的,不要留着IDE自動生成的,空的@param,@return,@throws 標記,讓代碼
    更簡潔。
    反例:方法名爲put,加上兩個有意義的變量名elephant和fridge,已經說明了這是在幹什麼,不需要任何額外的注
    釋。
    Rule 4.【推薦】避免創建人,創建日期,及更新日誌的註釋
    代碼後續還會有多人多次維護,而創建人可能會離職,讓我們相信源碼版本控制系統對更新記錄能做得更好。
    Rule 5. 【強制】代碼修改的同時,註釋也要進行相應的修改。尤其是參數、返回值、異常、核心邏輯等的修改
    Rule 6. 【強制】類、類的公有成員、方法的註釋必須使用Javadoc規範,使用/** xxx /格式,不得使用//xxx方式
    正確的JavaDoc格式可以在IDE中,查看調用方法時,不進入方法即可懸浮提示方法、參數、返回值的意義,提高閱
    讀效率。
    /
    *
  • put elephant into fridge.

@param elephant

  • @param fridge
  • @return
    */
    public void put(Elephant elephant, Fridge fridge);
    學習交流或獲取更多資料歡迎加入QQ羣:874514813Rule 7. 【推薦】JavaDoc中不要爲了HTML格式化而大量使用HTML標籤和轉義字符
    如果爲了Html版JavaDoc的顯示,大量使用<p> <pre>這樣的html標籤,以及&lt &quot 這樣的html轉義字符,嚴重
    影響了直接閱讀代碼時的直觀性,而直接閱讀代碼的機率其實比看Html版的JavaDoc大得多。
    另外IDE對JavaDoc的格式化也要求

    之類的標籤來換行,可以配置讓IDE不對JavaDoc的格式化。
    Rule 8. 【推薦】註釋不要爲了英文而英文
    如果沒有國際化要求,中文能表達得更清晰時還是用中文。
    Rule 9. 【推薦】TODO標記,清晰說明代辦事項和處理人
    清晰描述待修改的事項,保證過幾個月後仍然能夠清楚要做什麼修改。
    如果近期會處理的事項,寫明處理人。如果遠期的,寫明提出人。
    通過IDE和Sonar的標記掃描,經常清理此類標記,線上故障經常來源於這些標記但未處理的代碼。
    Sonar: Track uses of “TODO” tags
    Sonar: Track uses of “FIXME” tags
    Rule 10. 【推薦】合理處理註釋掉的代碼
    如果後續會恢復此段代碼,在目標代碼上方用///說明註釋動機,而不是簡單的註釋掉代碼。
    如果很大概率不再使用,則直接刪除(版本管理工具保存了歷史代碼)。
    Sonar: Sections of code should not be “commented out”
    (四) 方法設計
    Rule 1. 【推薦】方法的長度度量
    方法儘量不要超過100行,或其他團隊共同商定的行數。
    另外,方法長度超過8000個字節碼時,將不會被JIT編譯成二進制碼。
    正例:
    //TODO:calvin use xxx to replace yyy.
    反例:
    //TODO: refactor itSonar-107: Methods should not have too many lines,默認值改爲100
    Facebook-Contrib:Performance - This method is too long to be compiled by the JIT
    Rule 2. 【推薦】方法的語句在同一個抽象層級上
    反例:一個方法裏,前20行代碼在進行很複雜的基本價格計算,然後調用一個折扣計算函數,再調用一個贈品計算
    函數。
    此時可將前20行也封裝成一個價格計算函數,使整個方法在同一抽象層級上。
    Rule 3. 【推薦】爲了幫助閱讀及方法內聯,將小概率發生的異常處理及其他極小概率進入的代碼路徑,封裝成獨立
    的方法
    Rule 4. 【推薦】儘量減少重複的代碼,抽取方法
    超過5行以上重複的代碼,都可以考慮抽取公用的方法。
    Rule 5. 【推薦】方法參數最好不超過3個,最多不超過7個
    1)如果多個參數同屬於一個對象,直接傳遞對象。
    例外: 你不希望依賴整個對象,傳播了類之間的依賴性。
    2)將多個參數合併爲一個新創建的邏輯對象。
    例外: 多個參數之間毫無邏輯關聯。
    3)將函數拆分成多個函數,讓每個函數所需的參數減少。
    Sonar-107: Methods should not have too many parameters
    Rule 6.【推薦】下列情形,需要進行參數校驗
    1) 調用頻次低的方法。
    if(seldomHappenCase) {
    hanldMethod();
    } t
    ry {

    } catch(SeldomHappenException e) {
    handleException();
    }
    學習交流或獲取更多資料歡迎加入QQ羣:8745148132) 執行時間開銷很大的方法。此情形中,參數校驗時間幾乎可以忽略不計,但如果因爲參數錯誤導致中間執行回
    退,或者錯誤,代價更大。
    3) 需要極高穩定性和可用性的方法。
    4) 對外提供的開放接口,不管是RPC/HTTP/公共類庫的API接口。
    如果使用Apache Validate 或 Guava Precondition進行校驗,並附加錯誤提示信息時,注意不要每次校驗都做一次字符
    串拼接。
    Rule 7.【推薦】下列情形,不需要進行參數校驗
    1) 極有可能被循環調用的方法。
    2) 底層調用頻度比較高的方法。畢竟是像純淨水過濾的最後一道,參數錯誤不太可能到底層纔會暴露問題。
    比如,一般DAO層與Service層都在同一個應用中,所以DAO層的參數校驗,可以省略。
    3) 被聲明成private,或其他只會被自己代碼所調用的方法,如果能夠確定在調用方已經做過檢查,或者肯定不會
    有問題則可省略。
    即使忽略檢查,也儘量在方法說明裏註明參數的要求,比如vjkit中的@NotNull,@Nullable標識。
    Rule 8.【推薦】禁用assert做參數校驗
    assert斷言僅用於測試環境調試,無需在生產環境時進行的校驗。因爲它需要增加-ea啓動參數纔會被執行。而且校
    驗失敗會拋出一個AssertionError(屬於Error,需要捕獲Throwable)
    因此在生產環境進行的校驗,需要使用Apache Commons Lang的Validate或Guava的Precondition。
    Rule 9.【推薦】返回值可以爲Null,可以考慮使用JDK8的Optional類
    不強制返回空集合,或者空對象。但需要添加註釋充分說明什麼情況下會返回null值。
    本手冊明確防止NPE是調用者的責任。即使被調用方法返回空集合或者空對象,對調用者來說,也並非高枕無憂,必
    須考慮到遠程調用失敗、序列化失敗、運行時異常等場景返回null的情況。
    JDK8的Optional類的使用這裏不展開。
    Rule 10.【推薦】返回值可以爲內部數組和集合
    如果覺得被外部修改的可能性不大,或沒有影響時,不強制在返回前包裹成Immutable集合,或進行數組克隆。
    //WRONG
    Validate.isTrue(length > 2, “length is “+keys.length+”, less than 2”, length);
    //RIGHT
    Validate.isTrue(length > 2, “length is %d, less than 2”, length);Rule 11.【推薦】不能使用有繼承關係的參數類型來重載方法
    因爲方法重載的參數類型是根據編譯時表面類型匹配的,不根據運行時的實際類型匹配。
    Rule 12.【強制】正被外部調用的接口,不允許修改方法簽名,避免對接口的調用方產生影響
    只能新增新接口,並對已過時接口加@Deprecated註解,並清晰地說明新接口是什麼。
    Rule 13.【推薦】不使用@Deprecated的類或方法
    接口提供方既然明確是過時接口並提供新接口,那麼作爲調用方來說,有義務去考證過時方法的新實現是什麼。
    比如java.net.URLDecoder 中的方法decode(String encodeStr) 這個方法已經過時,應該使用雙參數decode(String source,
    String encode)。
    Rule 14.【推薦】不使用不穩定方法,如com.sun.包下的類,底層類庫中internal包下的類
    com.sun.
    ,sun.包下的類,或者底層類庫中名稱爲internal的包下的類,都是不對外暴露的,可隨時被改變的不穩
    定類。
    Sonar-1191: Classes from "sun.
    " packages should not be used
    (五) 類設計
    Rule 1. 【推薦】類成員與方法的可見性最小化
    任何類、方法、參數、變量,嚴控訪問範圍。過於寬泛的訪問範圍,不利於模塊解耦。思考:如果是一個private的
    方法,想刪除就刪除,可是一個public的service方法,或者一個public的成員變量,刪除一下,不得手心冒點汗嗎?
    例外:爲了單元測試,有時也可能將訪問範圍擴大,此時需要加上JavaDoc說明或vjkit中的@VisibleForTesting注
    解。
    class A {
    void hello(List list);
    void hello(ArrayList arrayList);
    } L
    ist arrayList = new ArrayList();
    // 下句調用的是hello(List list),因爲arrayList的定義類型是List
    a.hello(arrayList);
    學習交流或獲取更多資料歡迎加入QQ羣:874514813Rule 2.【推薦】 減少類之間的依賴
    比如如果A類只依賴B類的某個屬性,在構造函數和方法參數中,只傳入該屬性。讓閱讀者知道,A類只依賴了B類
    的這個屬性,而不依賴其他屬性,也不會調用B類的任何方法。
    Rule 3.【推薦】 定義變量與方法參數時,儘量使用接口而不是具體類
    使用接口可以保持一定的靈活性,也能向讀者更清晰的表達你的需求:變量和參數只是要求有一個Map,而不是特
    定要求一個HashMap。
    例外:如果變量和參數要求某種特殊類型的特性,則需要清晰定義該參數類型,同樣是爲了向讀者表達你的需求。
    Rule 4. 【推薦】類的長度度量
    類儘量不要超過300行,或其他團隊共同商定的行數。
    對過大的類進行分拆時,可考慮其內聚性,即類的屬性與類的方法的關聯程度,如果有些屬性沒有被大部分的方法
    使用,其內聚性是低的。
    Rule 5.【推薦】 構造函數如果有很多參數,且有多種參數組合時,建議使用Builder模式
    即使仍然使用構造函數,也建議使用chain constructor模式,逐層加入默認值傳遞調用,僅在參數最多的構造函數裏
    實現構造邏輯。
    Rule 6.【推薦】構造函數要簡單,尤其是存在繼承關係的時候
    可以將複雜邏輯,尤其是業務邏輯,抽取到獨立函數,如init(),start(),讓使用者顯式調用。
    a.foo(b); //WRONG
    a.foo(b.bar); //RIGHT
    Executor executor = new ThreadPoolBuilder().coreThread(10).queueLenth(100).build();
    public A(){
    A(DEFAULT_TIMEOUT);
    } p
    ublic A(int timeout) {

    } F
    oo foo = new Foo();
    foo.init();Rule 7.【強制】所有的子類覆寫方法,必須加@Override註解
    比如有時候子類的覆寫方法的拼寫有誤,或方法簽名有誤,導致沒能真正覆寫,加@Override可以準確判斷是否覆
    寫成功。
    而且,如果在父類中對方法簽名進行了修改,子類會馬上編譯報錯。
    另外,也能提醒閱讀者這是個覆寫方法。
    最後,建議在IDE的Save Action中配置自動添加@Override註解,如果無意間錯誤同名覆寫了父類方法也能被發現。
    Sonar-1161: “@Override” should be used on overriding and implementing methods
    Rule 8.【強制】靜態方法不能被子類覆寫。
    因爲它只會根據表面類型來決定調用的方法。
    Rule 9.靜態方法訪問的原則
    9.1【推薦】避免通過一個類的對象引用訪問此類的靜態變量或靜態方法,直接用類名來訪問即可
    目的是向讀者更清晰傳達調用的是靜態方法。可在IDE的Save Action中配置自動轉換。
    Sonar-2209: “static” members should be accessed statically
    Sonar-2440: Classes with only “static” methods should not be instantiated
    9.2 【推薦】除測試用例,不要static import 靜態方法
    靜態導入後忽略掉的類名,給閱讀者造成障礙。
    例外:測試環境中的assert語句,大家都太熟悉了。
    Sonar-3030: Classes should not have too many “static” imports 但IDEA經常自動轉換static import,所以暫不作爲規
    則。
    9.3【推薦】儘量避免在非靜態方法中修改靜態成員變量的值
    Sonar-2696: Instance methods should not write to “static” fields
    Base base = new Children();
    // 下句實際調用的是父類的靜態方法,雖然對象實例是子類的。
    base.staticMethod();
    int i = objectA.staticMethod(); // WRONG
    int i = ClassA.staticMethod(); // RIGHT
    // WRONG
    public void foo() {
    ClassA.staticFiled = 1;
    }
    學習交流或獲取更多資料歡迎加入QQ羣:874514813Sonar-3010: Static fields should not be updated in constructors
    Rule 10.【推薦】 內部類的定義原則
    當一個類與另一個類關聯非常緊密,處於從屬的關係,特別是只有該類會訪問它時,可定義成私有內部類以提高封
    裝性。
    另外,內部類也常用作回調函數類,在JDK8下建議寫成Lambda。
    內部類分匿名內部類,內部類,靜態內部類三種。

  1. 匿名內部類 與 內部類,按需使用:
    在性能上沒有區別;當內部類會被多個地方調用,或匿名內部類的長度太長,已影響對調用它的方法的閱讀時,定
    義有名字的內部類。
  2. 靜態內部類 與 內部類,優先使用靜態內部類:
  1. 非靜態內部類持有外部類的引用,能訪問外類的實例方法與屬性。構造時多傳入一個引用對性能沒有太大影響,
    更關鍵的是向閱讀者傳遞自己的意圖,內部類會否訪問外部類。
  2. 非靜態內部類裏不能定義static的屬性與方法。
    Sonar-2694: Inner classes which do not reference their owning classes should be “static”
    Sonar-1604: Anonymous inner classes containing only one method should become lambdas
    Rule 11.【推薦】使用getter/setter方法,還是直接public成員變量的原則。
    除非因爲特殊原因方法內聯失敗,否則使用getter方法與直接訪問成員變量的性能是一樣的。
    使用getter/setter,好處是可以進一步的處理:
  3. 通過隱藏setter方法使得成員變量只讀
  4. 增加簡單的校驗邏輯
  5. 增加簡單的值處理,值類型轉換等
    建議通過IDE生成getter/setter。
    但getter/seter中不應有複雜的業務處理,建議另外封裝函數,並且不要以getXX/setXX命名。
    如果是內部類,以及無邏輯的POJO/VO類,使用getter/setter除了讓一些純OO論者感覺舒服,沒有任何的好處,建
    議直接使用public成員變量。
    例外:有些序列化框架只能從getter/setter反射,不能直接反射public成員變量。
    Rule 12.【強制】POJO類必須覆寫toString方法。
    便於記錄日誌,排查問題時調用POJO的toString方法打印其屬性值。否則默認的Object.toString()只打印類名@數字的
    無效信息。
    Rule 13. hashCode和equals方法的處理,遵循如下規則:13.1【強制】只要重寫equals,就必須重寫hashCode。 而且選取相同的屬性進行運算。
    13.2【推薦】只選取真正能決定對象是否一致的屬性,而不是所有屬性,可以改善性能。
    13.3【推薦】對不可變對象,可以緩存hashCode值改善性能(比如String就是例子)。
    13.4【強制】類的屬性增加時,及時重新生成toString,hashCode和equals方法。
    Sonar-1206: “equals(Object obj)” and “hashCode()” should be overridden in pairs
    Rule 14.【強制】使用IDE生成toString,hashCode和equals方法。
    使用IDE生成而不是手寫,能保證toString有統一的格式,equals和hashCode則避免不正確的Null值處理。
    子類生成toString() 時,還需要勾選父類的屬性。
    Rule 15. 【強制】Object的equals方法容易拋空指針異常,應使用常量或確定非空的對象來調用equals
    推薦使用java.util.Objects#equals(JDK7引入的工具類)
    Sonar-1132: Strings literals should be placed on the left side when checking for equality
    Rule 16.【強制】除了保持兼容性的情況,總是移除無用屬性、方法與參數
    特別是private的屬性、方法、內部類,private方法上的參數,一旦無用立刻移除。信任代碼版本管理系統。
    Sonar-3985: Unused “private” classes should be removed
    Sonar-1068: Unused “private” fields should be removed
    Sonar: Unused “private” methods should be removed
    Sonar-1481: Unused local variables should be removed
    Sonar-1172: Unused method parameters should be removed Sonar-VJ版只對private方法的無用參數告警。
    Rule 17.【推薦】final關鍵字與性能無關,僅用於下列不可修改的場景
    1) 定義類及方法時,類不可繼承,方法不可覆寫;
    2) 定義基本類型的函數參數和變量,不可重新賦值;
    3) 定義對象型的函數參數和變量,僅表示變量所指向的對象不可修改,而對象自身的屬性是可以修改的。
    Rule 18.【推薦】得墨忒耳法則,不要和陌生人說話
    “test”.equals(object); //RIGHT
    Objects.equals(object, “test”); //RIGHT
    學習交流或獲取更多資料歡迎加入QQ羣:874514813以下調用,一是導致了對A對象的內部結構(B,C)的緊耦合,二是連串的調用很容易產生NPE,因此鏈式調用盡量不
    要過長。
    (六) 控制語句
    Rule 1. 【強制】if, else, for, do, while語句必須使用大括號,即使只有單條語句
    曾經試過合併代碼時,因爲沒加括號,單條語句合併成兩條語句後,仍然認爲只有單條語句,另一條語句在循環外
    執行。
    其他增加調試語句等情況也經常引起同樣錯誤。
    可在IDE的Save Action中配置自動添加。
    例外:一般由IDE生成的equals()函數
    Sonar-121: Control structures should use curly braces Sonar-VJ版豁免了equals()函數
    Rule 2.【推薦】少用if-else方式,多用哨兵語句式以減少嵌套層次
    Facebook-Contrib: Style - Method buries logic to the right (indented) more than it needs to be
    Rule 3.【推薦】限定方法的嵌套層次
    所有if/else/for/while/try的嵌套,當層次過多時,將引起巨大的閱讀障礙,因此一般推薦嵌套層次不超過4。
    通過抽取方法,或哨兵語句(見Rule 2)來減少嵌套。
    obj.getA().getB().getC().hello();
    if (a == b) {

    } i
    f (condition) {

    return obj;
    } /
    / 接着寫else的業務邏輯代碼;
    public void applyDriverLicense() {
    if (isTooYoung()) {
    System.out.println(“You are too young to apply driver license.”);Sonar-134: Control flow statements “if”, “for”, “while”, “switch” and “try” should not be nested too deeply,增大爲4
    Rule 4.【推薦】布爾表達式中的布爾運算符(&&,||)的個數不超過4個,將複雜邏輯判斷的結果賦值給一個有意義的
    布爾變量名,以提高可讀性
    Sonar-1067: Expressions should not be too complex,增大爲4
    Rule 5.【推薦】簡單邏輯,善用三元運算符,減少if-else語句的編寫
    Rule 6.【推薦】減少使用取反的邏輯
    不使用取反的邏輯,有利於快速理解。且大部分情況,取反邏輯存在對應的正向邏輯寫法。
    Sonar-1940: Boolean checks should not be inverted
    return;
    } i
    f (isTooOld()) {
    System.out.println(“You are too old to apply driver license.”);
    return;
    } S
    ystem.out.println(“You’ve applied the driver license successfully.”);
    return;
    } /
    /WRONG
    if ((file.open(fileName, “w”) != null) && (…) || (…)|| (…)) {

    } /
    /RIGHT
    boolean existed = (file.open(fileName, “w”) != null) && (…) || (…);
    if (existed || (…)) {

    } s
    != null ? s : “”;
    //WRONG
    if (!(x >= 268) { … }
    //RIGHT
    if (x < 268) { … }
    學習交流或獲取更多資料歡迎加入QQ羣:874514813Rule 7.【推薦】表達式中,能造成短路概率較大的邏輯儘量放前面,使得後面的判斷可以免於執行
    Rule 8.【強制】switch的規則
    1)在一個switch塊內,每個case要麼通過break/return等來終止,要麼註釋說明程序將繼續執行到哪一個case爲止;
    2)在一個switch塊內,都必須包含一個default語句並且放在最後,即使它什麼代碼也沒有。
    Sonar: “switch” statements should end with “default” clauses
    Rule 9.【推薦】循環體中的語句要考量性能,操作儘量移至循環體外處理
    1)不必要的耗時較大的對象構造;
    2)不必要的try-catch(除非出錯時需要循環下去)。
    Rule 10.【推薦】能用while循環實現的代碼,就不用do-while循環
    while語句能在循環開始的時候就看到循環條件,便於幫助理解循環內的代碼;
    do-while語句要在循環最後纔看到循環條件,不利於代碼維護,代碼邏輯容易出錯。
    (七) 基本類型與字符串
    if (maybeTrue() || maybeFalse()) { … }
    if (maybeFalse() && maybeTrue()) { … }
    String animal = “tomcat”;
    switch (animal) {
    case “cat”:
    System.out.println(“It’s a cat.”);
    break;
    case “lion”: // 執行到tiger
    case “tiger”:
    System.out.println(“It’s a beast.”);
    break;
    default:
    // 什麼都不做,也要有default
    break;
    }Rule 1. 原子數據類型(int等)與包裝類型(Integer等)的使用原則
    1.1 【推薦】需要序列化的POJO類屬性使用包裝數據類型
    1.2 【推薦】RPC方法的返回值和參數使用包裝數據類型
    1.3 【推薦】局部變量儘量使用基本數據類型
    包裝類型的壞處:
    1)Integer 24字節,而原子類型 int 4字節。
    2)包裝類型每次賦值還需要額外創建對象,如Integer var = 200, 除非數值在緩存區間內(見Integer.IntegerCache與
    Long.LongCache)纔會複用已緩存對象。默認緩存區間爲-128到127,其中Integer的緩存區間還受啓動參數的影響,
    如-XX:AutoBoxCacheMax=20000。
    3)包裝類型還有比較的陷阱(見規則3)
    包裝類型的好處:
    1)包裝類型能表達Null的語義。
    比如數據庫的查詢結果可能是null,如果用基本數據類型有NPE風險。又比如顯示成交總額漲跌情況,如果調用的
    RPC服務不成功時,應該返回null,顯示成-%,而不是0%。
    2)集合需要包裝類型,除非使用數組,或者特殊的原子類型集合。
    3)泛型需要包裝類型,如Result。
    Rule 2.原子數據類型與包裝類型的轉換原則
    2.1【推薦】自動轉換(AutoBoxing)有一定成本,調用者與被調用函數間儘量使用同一類型,減少默認轉換
    Sonar-2153: Boxing and unboxing should not be immediately reversed
    2.2 【推薦】自動拆箱有可能產生NPE,要注意處理
    Rule 3. 數值equals比較的原則
    //WRONG, sum 類型爲Long, i類型爲long,每次相加都需要AutoBoxing。
    Long sum=0L;
    for( long i = 0; i < 10000; i++) {
    sum+=i;
    } /
    /RIGHT, 準確使用API返回正確的類型
    Integer i = Integer.valueOf(str);
    int i = Integer.parseInt(str);
    //如果intObject爲null,產生NPE
    int i = intObject;
    學習交流或獲取更多資料歡迎加入QQ羣:8745148133.1【強制】 所有包裝類對象之間值的比較,全部使用equals方法比較
    ==判斷對象是否同一個。Integer var = ?在緩存區間的賦值(見規則1),會複用已有對象,因此這個區間內的
    Integer使用
    進行判斷可通過,但是區間之外的所有數據,則會在堆上新產生,不會通過。因此如果用== 來比較
    數值,很可能在小的測試數據中通過,而到了生產環境纔出問題。
    3.2【強制】 BigDecimal需要使用compareTo()
    因爲BigDecimal的equals()還會比對精度,2.0與2.00不一致。
    Facebook-Contrib: Correctness - Method calls BigDecimal.equals()
    3.3【強制】 Atomic* 系列,不能使用equals方法
    因爲 Atomic* 系列沒有覆寫equals方法。
    Sonar-2204: “.equals()” should not be used to test the values of “Atomic” classes
    3.4【強制】 double及float的比較,要特殊處理
    因爲精度問題,浮點數間的equals非常不可靠,在vjkit的NumberUtil中有對應的封裝函數。
    Sonar-1244: Floating point numbers should not be tested for equality
    Rule 4. 數字類型的計算原則
    4.1【強制】數字運算表達式,因爲先進行等式右邊的運算,再賦值給等式左邊的變量,所以等式兩邊的類型要一致
    例子1: int與int相除後,哪怕被賦值給float或double,結果仍然是四捨五入取整的int。
    需要強制將除數或被除數轉換爲float或double。
    例子2: int與int相乘,哪怕被賦值給long,仍然會溢出。
    需要強制將乘數的一方轉換爲long。
    //RIGHT
    if (counter1.get() == counter2.get()){…}
    float f1 = 0.15f;
    float f2 = 0.45f/3; //實際等於0.14999999
    //WRONG
    if (f1 == f2) {…}
    if (Double.compare(f1,f2)==0)
    //RIGHT
    static final float EPSILON = 0.00001f;
    if (Math.abs(f1-f2)<EPSILON) {…}
    double d = 24/7; //結果是3.0
    double d = (double)24/7; //結果是正確的3.42857另外,int的最大值約21億,留意可能溢出的情況。
    Sonar-2184: Math operands should be cast before assignment
    4.2【強制】數字取模的結果不一定是正數,負數取模的結果仍然負數
    取模做數組下標時,如果不處理負數的情況,很容易ArrayIndexOutOfBoundException。
    另外,Integer.MIN_VALUE取絕對值也仍然是負數。因此,vjkit的MathUtil對上述情況做了安全的封裝。
    Findbugs: Style - Remainder of hashCode could be negative
    4.3【推薦】 double 或 float 計算時有不可避免的精度問題
    儘量用double而不用float,但如果是金融貨幣的計算,則必須使用如下選擇:
    選項1, 使用性能較差的BigDecimal。BigDecimal還能精確控制四捨五入或是其他取捨的方式。
    選項2, 在預知小數精度的情況下,將浮點運算放大爲整數計數,比如貨幣以"分"而不是以"元"計算。
    Sonar-2164: Math should not be performed on floats
    Rule 5. 【推薦】如果變量值僅有有限的可選值,用枚舉類來定義常量
    尤其是變量還希望帶有名稱之外的延伸屬性時,如下例:
    long l = Integer.MAX_VALUE * 2; // 結果是溢出的-2
    long l = Integer.MAX_VALUE * 2L; //結果是正確的4294967294
    -4 % 3 = -1;
    Math.abs(Integer.MIN_VALUE) = -2147483648;
    float f = 0.45f/3; //結果是0.14999999
    double d1 = 0.45d/3; //結果是正確的0.15
    double d2 = 1.03d - 0.42d; //結果是0.6100000000000001
    //WRONG
    public String MONDAY = “MONDAY”;
    public int MONDAY_SEQ = 1;
    //RIGHT
    public enum SeasonEnum {
    SPRING(1), SUMMER(2), AUTUMN(3), WINTER(4);
    int seq;
    SeasonEnum(int seq) { this.seq = seq; }
    }
    學習交流或獲取更多資料歡迎加入QQ羣:874514813業務代碼中不要依賴ordinary()函數進行業務運算,而是自定義數字屬性,以免枚舉值的增減調序造成影響。 例
    外:永遠不會有變化的枚舉,比如上例的一年四季。
    Rule 6. 字符串拼接的原則
    6.1 【推薦】 當字符串拼接不在一個命令行內寫完,而是存在多次拼接時(比如循環),使用StringBuilder的append()
    反編譯出的字節碼文件顯示,其實每條用+進行字符拼接的語句,都會new出一個StringBuilder對象,然後進行
    append操作,最後通過toString方法返回String對象。所以上面兩個錯誤例子,會重複構造StringBuilder,重複
    toString()造成資源浪費。
    Sonar-1643: Strings should not be concatenated using ‘+’ in a loop
    6.2 【強制】 字符串拼接對象時,不要顯式調用對象的toString()
    如上,+實際是StringBuilder,本身會調用對象的toString(),且能很好的處理null的情況。
    6.3【強制】使用StringBuilder,而不是有所有方法都有同步修飾符的StringBuffer
    因爲內聯不成功,逃逸分析並不能抹除StringBuffer上的同步修飾符
    Sonar-1149: Synchronized classes Vector, Hashtable, Stack and StringBuffer should not be used
    6.4 【推薦】當拼接後字符串的長度遠大於16時,指定StringBuilder的大概長度,避免容量不足時的成倍擴展
    6.5 【推薦】如果字符串長度很大且頻繁拼接,可考慮ThreadLocal重用StringBuilder對象
    參考BigDecimal的toString()實現,及vjkit中的StringBuilderHolder。
    Rule 7. 【推薦】字符操作時,優先使用字符參數,而不是字符串,能提升性能
    String s = “hello” + str1 + str2; //Almost OK,除非初始長度有問題,見第3點.
    String s = “hello”; //WRONG
    if (condition) {
    s += str1;
    } S
    tring str = “start”; //WRONG
    for (int i = 0; i < 100; i++) {
    str = str + “hello”;
    } /
    /WRONG
    str = “result:” + myObject.toString(); // myObject爲Null時,拋NPE
    //RIGHT
    str = “result:” + myObject; // myObject爲Null時,輸出 result:null其他包括split等方法,在JDK String中未提供針對字符參數的方法,可考慮使用Apache Commons StringUtils 或Guava
    的Splitter。
    Sonar-3027: String function use should be optimized for single characters
    Rule 8. 【推薦】利用好正則表達式的預編譯功能,可以有效加快正則匹配速度
    反例:
    正例:
    (八) 集合處理
    Rule 1. 【推薦】底層數據結構是數組的集合,指定集合初始大小
    底層數據結構爲數組的集合包括 ArrayList,HashMap,HashSet,ArrayDequeue等。
    數組有大小限制,當超過容量時,需要進行復制式擴容,新申請一個是原來容量150% or 200%的數組,將原來的內
    容複製過去,同時浪費了內存與性能。HashMap/HashSet的擴容,還需要所有鍵值對重新落位,消耗更大。
    默認構造函數使用默認的數組大小,比如ArrayList默認大小爲10,HashMap爲16。因此建議使用ArrayList(int
    initialCapacity)等構造函數,明確初始化大小。
    HashMap/HashSet的初始值還要考慮加載因子:
    //WRONG
    str.indexOf(“e”);
    //RIGHT
    stringBuilder.append(‘a’);
    str.indexOf(‘e’);
    str.replace(‘m’,‘z’);
    //直接使用String的matches()方法
    result = “abc”.matches("[a-zA-z]");
    //每次重新構造Pattern
    Pattern pattern = Pattern.compile("[a-zA-z]");
    result = pattern.matcher(“abc”).matches();
    //在某個地方預先編譯Pattern,比如類的靜態變量
    private static Pattern pattern = Pattern.compile("[a-zA-z]");

    //真正使用Pattern的地方
    result = pattern.matcher(“abc”).matches();
    學習交流或獲取更多資料歡迎加入QQ羣:874514813爲了降低哈希衝突的概率(Key的哈希值按數組大小取模後,如果落在同一個數組下標上,將組成一條需要遍歷的
    Entry鏈),默認當HashMap中的鍵值對達到數組大小的75%時,即會觸發擴容。因此,如果預估容量是100,即需要
    設定100/0.75 +1=135的數組大小。vjkit的MapUtil的Map創建函數封裝了該計算。
    如果希望加快Key查找的時間,還可以進一步降低加載因子,加大初始大小,以降低哈希衝突的概率。
    Rule 2. 【推薦】儘量使用新式的foreach語法遍歷Collection與數組
    foreach是語法糖,遍歷集合的實際字節碼等價於基於Iterator的循環。
    foreach代碼一來代碼簡潔,二來有效避免了有多個循環或嵌套循環時,因爲不小心的複製粘貼,用錯了iterator或循
    環計數器(i,j)的情況。
    Rule 3. 【強制】不要在foreach循環裏進行元素的remove/add操作,remove元素可使用Iterator方式
    Facebook-Contrib: Correctness - Method modifies collection element while iterating
    Facebook-Contrib: Correctness - Method deletes collection element while iterating
    Rule 4. 【強制】使用entrySet遍歷Map類集合Key/Value,而不是keySet 方式進行遍歷
    keySet遍歷的方式,增加了N次用key獲取value的查詢。
    Sonar-2864:“entrySet()” should be iterated when both the key and value are needed
    Rule 5. 【強制】當對象用於集合時,下列情況需要重新實現hashCode()和 equals()
    1) 以對象做爲Map的KEY時;
    2) 將對象存入Set時。
    上述兩種情況,都需要使用hashCode和equals比較對象,默認的實現會比較是否同一個對象(對象的引用相等)。
    //WRONG
    for (String str : list) {
    if (condition) {
    list.remove(str);
    }
    } /
    /RIGHT
    Iterator it = list.iterator();
    while (it.hasNext()) {
    String str = it.next();
    if (condition) {
    it.remove();
    }
    }Map Key Value
    HashMap Nullable Nullable
    ConcurrentHashMap NotNull NotNull
    TreeMap NotNull Nullable
    另外,對象放入集合後,會影響hashCode(),equals()結果的屬性,將不允許修改。
    Sonar-2141:Classes that don’t define “hashCode()” should not be used in hashes
    Rule 6. 【強制】高度注意各種Map類集合Key/Value能不能存儲null值的情況
    由於HashMap的干擾,很多人認爲ConcurrentHashMap是可以置入null值。同理,Set中的value實際是Map中的key。
    Rule 7. 【強制】長生命週期的集合,裏面內容需要及時清理,避免內存泄漏
    長生命週期集合包括下面情況,都要小心處理。
    1) 靜態屬性定義;
    2) 長生命週期對象的屬性;
    3) 保存在ThreadLocal中的集合。
    如無法保證集合的大小是有限的,使用合適的緩存方案代替直接使用HashMap。
    另外,如果使用WeakHashMap保存對象,當對象本身失效時,就不會因爲它在集合中存在引用而阻止回收。但JDK
    的WeakHashMap並不支持併發版本,如果需要併發可使用Guava Cache的實現。
    Rule 8. 【強制】集合如果存在併發修改的場景,需要使用線程安全的版本
  1. 著名的反例,HashMap擴容時,遇到併發修改可能造成100%CPU佔用。
    推 薦 使 用 java.util.concurrent(JUC) 工 具 包 中 的 並 發 版 集 合 , 如 ConcurrentHashMap 等 , 優 於 使 用
    Collections.synchronizedXXX()系列函數進行同步化封裝(等價於在每個方法都加上synchronized關鍵字)。
    例外:ArrayList所對應的CopyOnWriteArrayList,每次更新時都會複製整個數組,只適合於讀多寫很少的場景。如
    果頻繁寫入,可能退化爲使用Collections.synchronizedList(list)。
  2. 即使線程安全類仍然要注意函數的正確使用。
    例如:即使用了ConcurrentHashMap,但直接是用get/put方法,仍然可能會多線程間互相覆蓋。
    //WRONG
    E e = map.get(key);
    if (e == null) {
    e = new E();
    map.put(key, e); //仍然能兩條線程併發執行put,互相覆蓋
    學習交流或獲取更多資料歡迎加入QQ羣:874514813Rule 9. 【推薦】正確使用集合泛型的通配符
    List並不是List的子類,如果希望泛型的集合能向上向下兼容轉型,而不僅僅適配唯一類,則需
    定義通配符,可以按需要extends 和 super的字面意義,也可以遵循PECS(Producer Extends Consumer Super)原則:
  3. 如果集合要被讀取,定義成<? extends T>
  4. 如果集合要被寫入,定義成<? super T>
    Rule 10. 【推薦】List, List<?> 與 List的選擇
    定義成List,會被IDE提示需要定義泛型。 如果實在無法確定泛型,就倉促定義成List<?>來矇混過關的話,該list
    只能讀,不能增改。定義成List呢,如規則10所述,List 並不是List的子類,除非函數
    定義使用了通配符。
    } r
    eturn e;
    //RIGHT
    E e = map.get(key);
    if (e == null) {
    e = new E();
    E previous = map.putIfAbsent(key, e);
    if(previous != null) {
    return previous;
    }
    } r
    eturn e;
    Class Stack{
    public void pushAll(Iterable<? extends E> src){
    for (E e: src)
    push(e);
    }
    } S
    tack stack = new Stack();
    Iterable integers = …;
    stack.pushAll(integers);
    Class Stack{
    public void popAll(Collection<? super E> dist){
    while(!isEmpty())
    dist.add(pop);
    }
    } S
    tack stack = new Stack();
    Collection objects = …;
    stack.popAll(objects);因此實在無法明確其泛型時,使用List也是可以的。
    Rule 11. 【推薦】如果Key只有有限的可選值,先將Key封裝成Enum,並使用EnumMap
    EnumMap , 以 Enum 爲 Key 的 Map , 內 部 存 儲 結 構 爲 Object[enum.size] , 訪 問 時 以 value =
    Object[enum.ordinal()]獲取值,同時具備HashMap的清晰結構與數組的性能。
    Sonar-1640: Maps with keys that are enum values should be replaced with EnumMap
    Rule 12. 【推薦】Array 與 List互轉的正確寫法
    Arrays.asList(array),如果array是原始類型數組如int[],會把整個array當作List的一個元素,String[] 或 Foo[]則無此
    問題。 Collections.addAll()實際是循環加入元素,性能相對較低,同樣會把int[]認作一個元素。
    Facebook-Contrib: Correctness - Impossible downcast of toArray() result
    Facebook-Contrib: Correctness - Method calls Array.asList on an array of primitive values
    public enum COLOR {
    RED, GREEN, BLUE, ORANGE;
    } E
    numMap<COLOR, String> moodMap = new EnumMap<COLOR, String> (COLOR.class);
    // list -> array,構造數組時不需要設定大小
    String[] array = (String[])list.toArray(); //WRONG;
    String[] array = list.toArray(new String[0]); //RIGHT
    String[] array = list.toArray(new String[list.size()]); //RIGHT,但list.size()可用0代替。
    // array -> list
    //非原始類型數組,且List不能再擴展
    List list = Arrays.asList(array);
    //非原始類型數組, 但希望List能再擴展
    List list = new ArrayList(array.length);
    Collections.addAll(list, array);
    //原始類型數組,JDK8
    List myList = Arrays.stream(intArray).boxed().collect(Collectors.toList());
    //原始類型數組,JDK7則要自己寫個循環來加入了
    學習交流或獲取更多資料歡迎加入QQ羣:874514813(九) 併發處理
    Rule 1. 【強制】創建線程或線程池時請指定有意義的線程名稱,方便出錯時回溯
    1)創建單條線程時直接指定線程名稱
    2) 線程池則使用guava或自行封裝的ThreadFactory,指定命名規則。
    Rule 2. 【推薦】儘量使用線程池來創建線程
    除特殊情況,儘量不要自行創建線程,更好的保護線程資源。
    同理,定時器也不要使用Timer,而應該使用ScheduledExecutorService。
    因爲Timer只有單線程,不能併發的執行多個在其中定義的任務,而且如果其中一個任務拋出異常,整個Timer也會
    掛掉,而ScheduledExecutorService只有那個沒捕獲到異常的任務不再定時執行,其他任務不受影響。
    Rule 3. 【強制】線程池不允許使用 Executors去創建,避資源耗盡風險
    Executors返回的線程池對象的弊端 :
    1)FixedThreadPool 和 SingleThreadPool:
    允許的請求隊列長度爲 Integer.MAX_VALUE,可能會堆積大量的請求,從而導致 OOM。
    2)CachedThreadPool 和 ScheduledThreadPool:
    允許的創建線程數量爲 Integer.MAX_VALUE,可能會創建大量的線程,從而導致 OOM。
    應通過 new ThreadPoolExecutor(xxx,xxx,xxx,xxx)這樣的方式,更加明確線程池的運行規則,合理設置Queue及線程
    池的core size和max size,建議使用vjkit封裝的ThreadPoolBuilder。
    Rule 4. 【強制】正確停止線程
    Thread t = new Thread();
    t.setName(“cleanup-thread”);
    //guava 或自行封裝的ThreadFactory
    ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat(threadNamePrefix + “-
    %d”).build();
    ThreadPoolExecutor executor = new ThreadPoolExecutor(…, threadFactory, …);
    //WRONG
    Thread thread = new Thread(…);
    thread.start();Thread.stop()不推薦使用,強行的退出太不安全,會導致邏輯不完整,操作不原子,已被定義成Deprecate方法。
    停止單條線程,執行Thread.interrupt()。
    停止線程池:
    ExecutorService.shutdown(): 不允許提交新任務,等待當前任務及隊列中的任務全部執行完畢後退出;
    ExecutorService.shutdownNow(): 通過Thread.interrupt()試圖停止所有正在執行的線程,並不再處理還在隊列中等
    待的任務。
    最優雅的退出方式是先執行shutdown(),再執行shutdownNow(),vjkit的ThreadPoolUtil進行了封裝。
    注意,Thread.interrupt()並不保證能中斷正在運行的線程,需編寫可中斷退出的Runnable,見規則5。
    Rule 5. 【強制】編寫可停止的Runnable
    執 行 Thread.interrupt() 時 , 如 果 線 程 處 於 sleep(), wait(), join(), lock.lockInterruptibly() 等 blocking 狀 態 , 會 拋 出
    InterruptedException,如果線程未處於上述狀態,則將線程狀態設爲interrupted。
    因此,如下的代碼無法中斷線程:
    5.1 正確處理InterruptException
    因爲InterruptException異常是個必須處理的Checked Exception,所以run()所調用的子函數很容易喫掉異常並簡單的
    處理成打印日誌,但這等於停止了中斷的傳遞,外層函數將收不到中斷請求,繼續原有循環或進入下一個堵塞。
    正確處理是調用Thread.currentThread().interrupt(); 將中斷往外傳遞。
    Sonar-2142: “InterruptedException” should not be ignored
    public void run() {
    while (true) { //WRONG,無判斷線程狀態。
    sleep();
    } p
    ublic void sleep() {
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    logger.warn(“Interrupted!”, e); //WRONG,喫掉了異常,interrupt狀態未再傳遞
    }
    }
    } /
    /RIGHT
    public void myMethod() {
    try {

    } catch (InterruptedException e) {
    Thread.currentThread().interrupt();
    }
    }
    學習交流或獲取更多資料歡迎加入QQ羣:8745148135.2 主循環及進入阻塞狀態前要判斷線程狀態
    其他如Thread.sleep()的代碼,在正式sleep前也會判斷線程狀態。
    Rule 6. 【強制】Runnable中必須捕獲一切異常
    如果Runnable中沒有捕獲RuntimeException而向外拋出,會發生下列情況:
  5. ScheduledExecutorService執行定時任務,任務會被中斷,該任務將不再定時調度,但線程池裏的線程還能用於其
    他任務。
  6. ExecutorService執行任務,當前線程會中斷,線程池需要創建新的線程來響應後續任務。
  7. 如果沒有在ThreadFactory設置自定義的UncaughtExceptionHanlder,則異常最終只打印在System.err,而不會打印
    在項目的日誌中。
    因 此 建 議 自 寫 的 Runnable 都 要 保 證 捕 獲 異 常 ; 如 果 是 第 三 方 的 Runnable , 可 以 將 其 再 包 裹 一 層 vjkit 中 的
    SafeRunnable。
    Rule 7. 【強制】全局的非線程安全的對象可考慮使用ThreadLocal存放
    全局變量包括單例對象,static成員變量。
    著名的非線程安全類包括SimpleDateFormat,MD5/SHA1的Digest。
    對這些類,需要每次使用時創建。
    但如果創建有一定成本,可以使用ThreadLocal存放並重用。
    ThreadLocal變量需要定義成static,並在每次使用前重置。
    //RIGHT
    public void run() {
    try {
    while (!Thread.isInterrupted()) {
    // do stuff
    }
    } catch (InterruptedException e) {
    logger.warn(“Interrupted!”, e);
    }
    } e
    xecutor.execute(ThreadPoolUtil.safeRunner(runner));
    private static final ThreadLocal SHA1_DIGEST = new ThreadLocal() {
    @Override
    protected MessageDigest initialValue() {
    try {
    return MessageDigest.getInstance(“SHA”);Sonar-2885: Non-thread-safe fields should not be static
    Facebook-Contrib: Correctness - Field is an instance based ThreadLocal variable
    Rule 8. 【推薦】縮短鎖
    1) 能鎖區塊,就不要鎖整個方法體;
    2)能用對象鎖,就不要用類鎖。
    Rule 10. 【推薦】選擇分離鎖,分散鎖甚至無鎖的數據結構
    分離鎖:
    1) 讀寫分離鎖ReentrantReadWriteLock,讀讀之間不加鎖,僅在寫讀和寫寫之間加鎖;
    2) Array Base的queue一般是全局一把鎖,而Linked Base的queue一般是隊頭隊尾兩把鎖。
    } catch (NoSuchAlgorithmException e) {
    throw new RuntimeException("…", e);
    }
    }
    };
    public void digest(byte[] input) {
    MessageDigest digest = SHA1_DIGEST.get();
    digest.reset();
    return digest.digest(input);
    } /
    /鎖整個方法,等價於整個方法體內synchronized(this)
    public synchronized boolean foo(){};
    //鎖區塊方法,僅對需要保護的原子操作的連續代碼塊進行加鎖。
    public boolean foo() {
    synchronized(this) {


    } /
    /other stuff
    } /
    /對象鎖,隻影響使用同一個對象加鎖的線程
    synchronized(this) {

    } /
    /類鎖,使用類對象作爲鎖對象,影響所有線程。
    synchronized(A.class) {

    }
    學習交流或獲取更多資料歡迎加入QQ羣:874514813分散鎖(又稱分段鎖):
    1)如JDK7的ConcurrentHashMap,分散成16把鎖;
    2)對於經常寫,少量讀的計數器,推薦使用JDK8或vjkit封裝的LongAdder對象性能更好(內部分散成多個
    counter,減少樂觀鎖的使用,取值時再相加所有counter)
    無鎖的數據結構:
    1)完全無鎖無等待的結構,如JDK8的ConcurrentHashMap;
    2)基於CAS的無鎖有等待的數據結構,如AtomicXXX系列。
    Rule 11. 【推薦】基於ThreadLocal來避免鎖
    比 如 Random 實 例 雖 然 是 線 程 安 全 的 , 但 其 實 它 的 seed 的 訪 問 是 有 鎖 保 護 的 。 因 此 建 議 使 用 JDK7 的
    ThreadLocalRandom,通過在每個線程裏放一個seed來避免了加鎖。
    Rule 12. 【推薦】規避死鎖風險
    對多個資源多個對象的加鎖順序要一致。
    如果無法確定完全避免死鎖,可以使用帶超時控制的tryLock語句加鎖。
    Rule 13. 【推薦】volatile修飾符,AtomicXX系列的正確使用
    多線程共享的對象,在單一線程內的修改並不保證對所有線程可見。使用volatile定義變量可以解決(解決了可見
    性)。
    但是如果多條線程併發進行基於當前值的修改,如併發的counter++,volatile則無能爲力(解決不了原子性)。
    此時可使用Atomic*系列:
    但如果需要原子地同時對多個AtomicXXX的Counter進行操作,則仍然需要使用synchronized將改動代碼塊加鎖。
    Rule 14. 【推薦】延時初始化的正確寫法
    通過雙重檢查鎖(double-checked locking)實現延遲初始化存在隱患,需要將目標屬性聲明爲volatile型,爲了更高
    的性能,還要把volatile屬性賦予給臨時變量,寫法複雜。
    所以如果只是想簡單的延遲初始化,可用下面的靜態類的做法,利用JDK本身的class加載機制保證唯一初始化。
    AtomicInteger count = new AtomicInteger();
    count.addAndGet(2);Sonar-2168: Double-checked locking should not be used
    (十) 異常處理
    Rule 1. 【強制】創建異常的消耗大,只用在真正異常的場景
    構造異常時,需要獲得整個調用棧,有一定消耗。
    不要用來做流程控制,條件控制,因爲異常的處理效率比條件判斷低。
    發生概率較高的條件,應該先進行檢查規避,比如:IndexOutOfBoundsException,NullPointerException等,所以如
    果代碼裏捕獲這些異常通常是個壞味道。
    Sonar-1696: “NullPointerException” should not be caught
    Rule 2. 【推薦】在特定場景,避免每次構造異常
    如上,異常的構造函數需要獲得整個調用棧。
    如果異常頻繁發生,且不需要打印完整的調用棧時,可以考慮繞過異常的構造函數。
    1) 如果異常的message不變,將異常定義爲靜態成員變量;
    下例定義靜態異常,並簡單定義一層的StackTrace。ExceptionUtil見vjkit。
    private static class LazyObjectHolder {
    static final LazyObject instance = new LazyObject();
    } p
    ublic void myMethod() {
    LazyObjectHolder.instance.doSomething();
    } /
    /WRONG
    try {
    return obj.method();
    } catch (NullPointerException e) {
    return false;
    } /
    /RIGHT
    if (obj == null) {
    return false;
    }
    學習交流或獲取更多資料歡迎加入QQ羣:8745148132) 如果異常的message會變化,則對靜態的異常實例進行clone()再修改message。
    Exception默認不是Cloneable的,CloneableException見vjkit。
    3)自定義異常,也可以考慮重載fillStackTrace()爲空函數,但相對沒那麼靈活,比如無法按場景指定一層的
    StackTrace。
    Rule 3. 【推薦】自定義異常,建議繼承RuntimeException
    詳見《Clean Code》,爭論已經結束,不再推薦原本初衷很好的CheckedException。
    因爲CheckedException需要在拋出異常的地方,與捕獲處理異常的地方之間,層層定義throws XXX來傳遞
    Exception,如果底層代碼改動,將影響所有上層函數的簽名,導致編譯出錯,對封裝的破壞嚴重。對
    CheckedException的處理也給上層程序員帶來了額外的負擔。因此其他語言都沒有CheckedException的設計。
    Rule 4. 【推薦】異常日誌應包含排查問題的足夠信息
    異常信息應包含排查問題時足夠的上下文信息。
    捕獲異常並記錄異常日誌的地方,同樣需要記錄沒有包含在異常信息中,而排查問題需要的信息,比如捕獲處的上
    下文信息。
    Facebook-Contrib: Style - Method throws exception with static message string
    private static RuntimeException TIMEOUT_EXCEPTION = ExceptionUtil.setStackTrace(new
    RuntimeException(“Timeout”),
    MyClass.class, “mymethod”);

    throw TIMEOUT_EXCEPTION;
    private static CloneableException TIMEOUT_EXCEPTION = new CloneableException(“Timeout”)
    .setStackTrace(My.class,
    “hello”);

    throw TIMEOUT_EXCEPTION.clone(“Timeout for 40ms”);
    //WRONG
    new TimeoutException(“timeout”);
    logger.error(e.getMessage(), e);
    //RIGHT
    new TimeoutException(“timeout:” + eclapsedTime + “, configuration:” + configTime);
    logger.error(“user[” + userId + “] expired:” + e.getMessage(), e);Rule 5. 異常拋出的原則
    5.1 【推薦】儘量使用JDK標準異常,項目標準異常
    盡 量 使 用 JDK 標 準 的 Runtime 異 常 如 IllegalArgumentException , IllegalStateException ,
    UnsupportedOperationException,項目定義的Exception如ServiceException。
    5.2 【推薦】根據調用者的需要來定義異常類,直接使用RuntimeException是允許的
    是 否 定 義 獨 立 的 異 常 類 , 關 鍵 是 調 用 者 會 如 何 處 理 這 個 異 常 , 如 果 沒 有 需 要 特 別 的 處 理 , 直 接 拋 出
    RuntimeException也是允許的。
    Rule 6. 異常捕獲的原則
    6.1 【推薦】按需要捕獲異常,捕獲Exception或Throwable是允許的
    如果無特殊處理邏輯,統一捕獲Exception統一處理是允許的。
    捕獲Throwable是爲了捕獲Error類異常,包括其實無法處理的OOM StackOverflow ThreadDeath,以及類加載,反射
    時可能拋出的NoSuchMethodError NoClassDefFoundError等。
    6.2【推薦】多個異常的處理邏輯一致時,使用JDK7的語法避免重複代碼
    Sonar-2147: Catches should be combined
    Rule 7.異常處理的原則
    7.1 【強制】捕獲異常一定要處理;如果故意捕獲並忽略異常,須要註釋寫明原因
    方便後面的閱讀者知道,此處不是漏了處理。
    7.2 【強制】異常處理不能吞掉原異常,要麼在日誌打印,要麼在重新拋出的異常裏包含原異常
    try {

    } catch (AException | BException | CException ex) {
    handleException(ex);
    } /
    /WRONG
    try {
    } catch(Exception e) {
    } /
    /RIGHT
    try {
    } catch(Exception ignoredExcetpion) {
    //continue the loop
    }
    學習交流或獲取更多資料歡迎加入QQ羣:874514813Sonar-1166: Exception handlers should preserve the original exceptions , 其 中 默 認 包 含 了 InterruptedException,
    NumberFormatException,NoSuchMethodException等若干例外
    7.3 【強制】如果不想處理異常,可以不進行捕獲。但最外層的業務使用者,必須處理異常,將其轉化爲用戶可以
    理解的內容
    Rule 8. finally塊的處理原則
    8.1 【強制】必須對資源對象、流對象進行關閉,或使用語法try-with-resource
    關閉動作必需放在finally塊,不能放在try塊 或 catch塊,這是經典的錯誤。
    更加推薦直接使用JDK7的try-with-resource語法自動關閉Closeable的資源,無需在finally塊處理,避免潛在問題。
    8.2 【強制】如果處理過程中有拋出異常的可能,也要做try-catch,否則finally塊中拋出的異常,將代替try塊中拋
    出的異常
    Sonar-1163: Exceptions should not be thrown in finally blocks
    8.3 【強制】不能在finally塊中使用return,finally塊中的return將代替try塊中的return及throw Exception
    //WRONG
    throw new MyException(“message”);
    //RIGHT 記錄日誌後拋出新異常,向上次調用者屏蔽底層異常
    logger.error(“message”, ex);
    throw new MyException(“message”);
    //RIGHT 傳遞底層異常
    throw new MyException(“message”, ex);
    try (Writer writer = …) {
    writer.append(content);
    } /
    /WRONG
    try {

    throw new TimeoutException();
    } finally {
    file.close();//如果file.close()拋出IOException, 將代替TimeoutException
    } /
    /RIGHT, 在finally塊中try-catch
    try {

    throw new TimeoutException();
    } finally {
    IOUtil.closeQuietly(file); //該方法中對所有異常進行了捕獲
    } /
    /WRONGSonar-1143: Jump statements should not occur in “finally” blocks
    (十一) 日誌規約
    Rule 1. 【強制】應用中不可直接使用日誌庫(Log4j、Logback)中的API,而應使用日誌框架SLF4J中的API
    使用門面模式的日誌框架,有利於維護各個類的日誌處理方式統一。
    Rule 2. 【推薦】對不確定會否輸出的日誌,採用佔位符或條件判斷
    如果日誌級別是info,上述日誌不會打印,但是會執行1)字符串拼接操作,2)如果symbol是對象,還會執行toString()
    方法,浪費了系統資源,最終日誌卻沒有打印。
    但如果symbol.getMessage()本身是個消耗較大的動作,佔位符在此時並沒有幫助,須要改爲條件判斷方式來完全避
    免它的執行。
    try {

    return 1;
    } finally {
    return 2; //實際return 2 而不是1
    } t
    ry {

    throw TimeoutException();
    } finally {
    return 2; //實際return 2 而不是TimeoutException
    } i
    mport org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    private static Logger logger = LoggerFactory.getLogger(Foo.class);
    //WRONG
    logger.debug("Processing trade with id: " + id + " symbol: " + symbol);
    //RIGHT
    logger.debug("Processing trade with id: {} symbol : {} ", id, symbol);
    學習交流或獲取更多資料歡迎加入QQ羣:874514813Rule 3. 【推薦】對確定輸出,而且頻繁輸出的日誌,採用直接拼裝字符串的方式
    如果這是一條WARN,ERROR級別的日誌,或者確定輸出的INFO級別的業務日誌,直接字符串拼接,比使用佔位
    符替換,更加高效。
    Slf4j的佔位符並沒有魔術,每次輸出日誌都要進行佔位符的查找,字符串的切割與重新拼接。
    Rule 4. 【推薦】儘量使用異步日誌
    低延時的應用,使用異步輸出的形式(以AsyncAppender串接真正的Appender),可減少IO造成的停頓。
    需要正確配置異步隊列長度及隊列滿的行爲,是丟棄還是等待可用,業務上允許丟棄的儘量選丟棄。
    Rule 5. 【強制】禁止使用性能很低的System.out()打印日誌信息
    同理也禁止e.printStackTrace();
    例外: 應用啓動和關閉時,擔心日誌框架還未初始化或已關閉。
    Sonar-106: Standard outputs should not be used directly to log anything
    Sonar-1148: Throwable.printStackTrace(…) should not be called
    Rule 6. 【強制】禁止配置日誌框架輸出日誌打印處的類名,方法名及行號的信息
    日誌框架在每次打印時,通過主動獲得當前線程的StackTrace來獲取上述信息的消耗非常大,儘量通過Logger名本
    身給出足夠信息。
    Rule 7. 【推薦】謹慎地記錄日誌,避免大量輸出無效日誌,信息不全的日誌
    大量地輸出無效日誌,不利於系統性能,也不利於快速定位錯誤點。
    //WRONG
    logger.debug("Processing trade with id: {} symbol : {} ", id, symbol.getMessage());
    //RIGHT
    if (logger.isDebugEnabled()) {
    logger.debug("Processing trade with id: " + id + " symbol: " + symbol.getMessage());
    } /
    /RIGHT
    logger.info("I am a business log with id: " + id + " symbol: " + symbol);
    //RIGHT
    logger.warn(“Processing trade with id: " + id + " symbol: " + symbol);記錄日誌時請思考:這些日誌真的有人看嗎?看到這條日誌你能做什麼?能不能給問題排查帶來好處?
    Rule 8. 【推薦】使用warn級別而不是error級別,記錄外部輸入參數錯誤的情況
    如非必要,請不在此場景打印error級別日誌,避免頻繁報警。
    error級別只記錄系統邏輯出錯、異常或重要的錯誤信息。
    (十二) 其他規約
    Rule 1. 【參考】儘量不要讓魔法值(即未經定義的數字或字符串常量)直接出現在代碼中
    例外:-1,0,1,2,3 不認爲是魔法數
    Sonar-109: Magic numbers should not be used 但現實中所謂魔法數還是太多,該規則不能被真正執行。
    Rule 2. 【推薦】時間獲取的原則
    1)獲取當前毫秒數System.currentTimeMillis() 而不是new Date().getTime(),後者的消耗要大得多。
    2)如果要獲得更精確的,且不受NTP時間調整影響的流逝時間,使用System.nanoTime()獲得機器從啓動到現在流
    逝的納秒數。
    3)如果希望在測試用例中控制當前時間的值,則使用vjkit的Clock類封裝,在測試和生產環境中使用不同的實現。
    Rule 3. 【推薦】變量聲明儘量靠近使用的分支
    不要在一個代碼塊的開頭把局部變量一次性都聲明瞭(這是c語言的做法),而是在第一次需要使用它時才聲明。
    否則如果方法已經退出或進入其他分支,就白白初始化了變量。
    //WRONG
    String key = “Id#taobao_”+tradeId;
    cache.put(key, value);
    //WRONG
    Foo foo = new Foo();
    if(ok){
    return;
    } f
    oo.bar();
    學習交流或獲取更多資料歡迎加入QQ羣:874514813Rule 4. 【推薦】不要像C那樣一行裏做多件事情
    Sonar-1659: Multiple variables should not be declared on the same line
    Rule 5. 【推薦】不要爲了性能而使用JNI本地方法
    Java在JIT後並不比C代碼慢,JNI方法因爲要反覆跨越JNI與Java的邊界反而有額外的性能損耗。
    因此JNI方法僅建議用於調用"JDK所沒有包括的, 對特定操作系統的系統調用”
    Rule 6. 【推薦】正確使用反射,減少性能損耗
    獲取Method/Field對象的性能消耗較大, 而如果對Method與Field對象進行緩存再反覆調用,則並不會比直接調用類的
    方法與成員變量慢(前15次使用NativeAccessor,第15次後會生成GeneratedAccessorXXX,bytecode爲直接調用實際
    方法)
    Rule 7.【推薦】可降低優先級的常見代碼檢查規則
  1. 接口內容的定義中,去除所有modifier,如public等。 (多個public也沒啥,反正大家都看慣了)
  2. 工具類,定義private構造函數使其不能被實例化。(命名清晰的工具類,也沒人會去實例化它,對靜態方法通過
    類來訪問也能避免實例化)
    《阿里Java開發手冊》定製紀錄
    只記錄較大的改動,對更多條目內容的重新組織與擴寫,則未一一冗述。
    //WRONG
    fooBar.fChar = barFoo.lchar = ‘c’;
    argv++; argc–;
    int level, size;
    //用於對同一個方法多次調用
    private Method method = …
    public void foo(){
    method.invoke(obj, args);
    } /
    /用於僅會對同一個方法單次調用
    ReflectionUtils.invoke(obj, methodName, args);VIP 規範 阿里規範 修改
  3. 變量、參數重名覆蓋 新增規則
  4. 禁止拼音縮寫 2. 嚴禁使用拼音與英文混合的方式 改寫規則
  5. 禁用其他編程語言風格的前
    綴和後綴
  6. 代碼中的命名均不能以下劃線或美元符
    號開始
    擴寫規則,把其他語言的囉嗦都禁
    止掉
  7. 命名的好壞,在於其“模糊
    度”
  8. 爲了達到代碼自解釋的目標 擴寫規則,參考《Clean Code》的
    更多例子
  9. 常量命名全部大寫 5.常量名大寫 擴寫規則
  10. 類型與中括號緊挨相連來定義數組 刪除規則,非命名風格,也不重要
  11. 接口類中的方法和成員變量不要加任
    何修飾符號
    移動規則,非命名風格,移到類設
  12. 各層命名規約 刪除規則,各公司有自己的習慣
    VIP 規範 阿里規範 修改
  13. 項目組統一的代碼格式模板 規則1-8 用IDE模版代替逐條描述
    同時對Tab/空格不做硬性規定
  14. 用小括號來限定運算優先級 新增規則
  15. 類內方法定義的順序 新增規則
  16. 通過空行進行邏輯分段 11. 不同邏輯、不同語義 改寫規則
  17. 避免IDE格式化 新增規則
    10.單個方法行數不超過80行 刪除規則,非格式規約,移動方法設計
    11.沒有必要增加若干空格來對齊 刪除規則,現在很少人這麼做
    《唯品會Java開發手冊》-與阿里手冊的比較文學I
    (一) 命名規約
    對應 阿里規範《命名風格》一章
    (二) 格式規約
    對應 阿里規範《代碼格式》一章
    學習交流或獲取更多資料歡迎加入QQ羣:874514813VIP 規範 阿里規範 修改
  18. 刪除空註釋,無意義註釋 增加規則
  19. JavaDoc中不要大量使用HTML標籤和
    轉義字符
    增加規則
  20. 註釋的基本要求 9. 對於註釋的要求 擴寫規則
    4.避免創建人的註釋 3.所有的類都必須添加創建者 衝突規則
    2.所有的抽象方法必須用
    Javadoc註釋
    刪除規則,因爲規則2不強制,並
    入規則1
    4.方法內部單行註釋,使用//注

    刪除規則,區別不大不強求
  21. 所有的枚舉類型字段必須要
    有註釋
    刪除規則,因爲規則2不強制
    VIP 規範 阿里規範 修改
    2.減少類之間的依賴 增加規則
    3.定義變量與方法參數時,儘量使用
    接口
    增加規則
    (三) 註釋規約
    對應 阿里規範《註釋規約》一章
    (四) 方法設計
    規則 6,7,12,13 從阿里規範《控制語句》一章 移入
    規則 9 從阿里規範《異常處理》一章 移入
    規則1,2,3,4,5,8,10,11,14爲新建規則
    (五) 類設計
    對應 阿里規範《OOP規範》一章VIP 規範 阿里規範 修改
    4.類的長度度量 增加規則
    5.Builder模式 增加規則
    8.靜態方法不能被覆寫 增加規則
    9.靜態方法的訪問原則 擴寫規則
    10.內部類原則 增加規則
    12-14.hashCode,equals,toString的
    規則
    增加規則
    16.總是移除無用屬性、方法與參數 增加規則
    18.【推薦】得墨忒耳法則 增加規則
  22. 提倡同學們儘量不用可變參數編程 刪除規則
  23. 定義DO/DTO/VO等POJO類時,不要設定任何屬性
    默認值
    刪除規則
  24. 序列化類新增屬性時,請不要修改
    serialVersionUID字段
    刪除規則
  25. 使用索引訪問用String的split方法時 刪除規則
  26. 慎用Object的clone方法來拷貝對象 刪除規則
    規則4,5 移到《方法規
    約》
    規則6 移到《通用設
    計》
    規則7,8,17 移到《基礎類
    型》
    規則14,15 移到《格式規
    約》
    VIP 規範 阿里規範 修改
    4.布爾表達式中的運算符個數不超過4個 擴寫規則
    (六) 控制語句
    對應 阿里規範《控制語句》一章
    學習交流或獲取更多資料歡迎加入QQ羣:874514813VIP 規範 阿里規範 修改
    5.善用三元運算符 增加規則
    6.能造成短路概率較大的邏輯放前面 增加規則
    10.能用while循環實現的代碼,就不用do-while
    循環
    增加規則
  27. 在高併發場景中,避免使用 ”等於”作爲
    條件
    刪除規則
  28. 接口入參保護 刪除規則
  29. 下列情形,需要進行參數校驗 移到《方法規
    約》
  30. 下列情形,不需要進行參數校 移到《方法規
    約》
    VIP 規範 阿里規範 修改
  31. foreach語法遍歷 增加規則
  32. 長生命週期的集合 增加規則
  33. 併發集合 增加規則
  34. 泛型的通配符 增加規則
  35. List, List<?> 與
    List的選擇
    增加規則
  36. EnumMap 增加規則
    (七) 基本類型與字符串
    規則1,3 從 阿里規範《OOP規範》移入,並對2進行擴寫
    規則5 從 阿里規範《常量定義》移入並擴寫
    規則8 從 阿里規範《其他》移入
    規則4,6,7爲新建規則
    (八) 集合處理
    對應 阿里規範《集合處理》一章VIP 規範 阿里規範 修改
  37. ArrayList的subList結果 刪除規則
  38. 泛型通配符 刪除規則
    12.合理利用好集合的有序性 刪除規則
    13.利用Set元素唯一的特性 刪除規則
    12.Array 與 List互轉的 使用集合轉數組的方法,必須使用集合
    的toArray
    某位老大的測試,new String[0]
    也不錯
    VIP 規範 阿里規範 修改
  39. 指定線程名 擴寫規則
  40. 正確停止線程 擴寫規則
  41. 編寫可中斷的Runnable 增加規則
  42. Runnable中必須捕獲一切異常 9.多線程並行處理定時任務 擴寫規則
  43. 全局變量的線程安全 擴寫規則
  44. 選擇分離鎖, 分散鎖甚至無鎖的數據結

    增加規則
  45. volatile修飾符,AtomicXX系列的正確
    使用
    擴寫規則
    8.併發修改同一記錄時,需要加鎖 刪除規則
    10.使用CountDownLatch進行異步轉同步
    操作
    刪除規則
    14.HashMap在容量不夠進行resize 移到《集合規約》一
  46. 延時初始化的正確寫法 12.雙重檢查鎖 衝突規則
    (九) 併發處理: 併發與多線程
    對應 阿里規範《併發處理》一章
    (十) 異常處理
    學習交流或獲取更多資料歡迎加入QQ羣:874514813VIP 規範 阿里規範 修改
    2.在特定場合,避免每次構造
    異常
    增加規則
    5.異常拋出的原則 增加規則
    6.異常捕獲的原則 增加規則
    7.異常處理的原則 增加規則
    8.捕獲異常與拋異常,必須是完全匹配 刪除規則
    12.對於公司外的開放接口必須使用“錯
    誤碼”
    刪除規則
    13.DRY原則 刪除規則,爲什麼會出現在這章,太
    著名了
    9.返回值可以爲null 移到《方法設計》一章
  47. 【推薦】防止NPE,是程序員的基
    本修養
    拆開到各章
    11.避免直接拋出RuntimeException 規則衝突
    VIP 規範 阿里規範 修改
    4.儘量使用異步日誌 增加規則
    5.禁止使用System.out() 增加規則
    6.禁止配置日誌框架打印日誌打印時的類
    名,行號等信息
    增加規則
    2.日誌文件推薦至少保存15天 刪除規則
    3.應用中的擴展日誌命名方式 刪除規則
    6.異常信息應該包括兩類信息 移到《異常處理》
    對應 阿里規範《異常處理》一章
    (十一) 日誌規約
    對應 阿里規範《日誌規約》一章VIP 規範 阿里規範 修改
    2.合理使用使用佔位符 4.對trace/debug/info級別的日誌使
    用佔位符
    還是要判斷日誌是否必然輸
    出,
    並強調條件判斷與佔位符之
    間的差別
    VIP 規範 阿里規範 修改
    規則2-4 刪除規則
    規則5. 如果變量值僅在一個固定範圍內變化用enum類型來定義 移到《基本類型》
    VIP 規範 阿里規範 修改
    規則2-4,6-8 刪除規則
    規則1. 在使用正則表達式時,利用好其預編譯功 移到《基本類型》
    (十二) 其他規約
    保留 阿里規範《常量定義》一章的規則1
    保留 阿里規範《其他》一章的規則7
    規則3,4,5,6,7均爲新增規則
    學習交流或獲取更多資料歡迎加入QQ羣:874514813
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章