阿里java開發手冊學習筆記(一、 編程規約)

(一)命名風格

  1. 代碼中的命名不能以下劃線或美元符號開始或結束
  2. 代碼命名不能舒勇拼音與英文混合,更不得實用純中文命名
  3. 類名實用駝峯式命名,首字母大寫
  4. 方法名,參數名,成員變量名採用駝峯式命名,首字母小寫
  5. 常量命名全部大寫,單詞間下劃線分隔。語義表達清楚
  6. 抽象類命名Abstract或Base開頭,異常類實用Exception結尾,測試類命名以它要測試的類名開始,以Test結尾
  7. 類型與中括號緊相連表示數組
  8. POJO類中布爾類型的變量都不要加is前綴。
    在這裏插入圖片描述
  9. 包名統一用小寫,包名統一用單數形式,類名可以使用複數形式,點分隔符之間僅有一個自然語義的英語單詞
  10. 子父類的成員變量之間避免重名
  11. 杜絕不規範命名,避免望文不知義
  12. 任何元素命名時,使用盡量完整的單詞組合來表達其意義
  13. 常量與變量命名時,表示類型的名詞放在前面如:nameList
  14. 如果模塊,接口,類,方法使用了設計模式,在命名時需要體現出具體模式
  15. 接口類中的方法和屬性不要加任何修飾符號
  16. 對於Service和Dao類,基於SOA的理念,暴漏出來的一定是接口,內部的實現類用Impl的後綴與接口區別
  17. 如果是形容能力的的接口名稱,取對應的形容詞爲接口名(通常爲-able的形容詞)
  18. Service/Dao方法命名規則
    前綴|方法
    -----|------
    get|獲取單個對象
    list|過去對象列表
    count|統計值
    save/insert|插入
    remove/delete|刪除
    updata|修改

(二)常量定義

  1. 不允許任何魔法值(未經預先定義的常量)直接出現在代碼中
  2. 在long或Long賦值時,數值後使用大寫的L
  3. 不要使用一個常量類維護所有常量,要按常量功能進行歸類,分開維護
  4. 常量的複用層次有五層:跨應用共享常量、應用內共享常量、子工程內共享常量、
    包內共享常量、類內共享常量。
    1) 跨應用共享常量:放置在二方庫中,通常是 client.jar 中的 constant 目錄下。
    2) 應用內共享常量:放置在一方庫中,通常是子模塊中的 constant 目錄下
  5. 如果變量值僅在一個固定範圍內變化用 enum 類型來定義

(三)代碼格式

  1. 【強制】如果是大括號內爲空,則簡潔地寫成{}即可,大括號中間無需換行和空格;如果是非
    空代碼塊則:
    1) 左大括號前不換行。
    2) 左大括號後換行。
    3) 右大括號前換行。
    4) 右大括號後還有 else 等代碼則不換行;表示終止的右大括號後必須換行。
  2. 【強制】左小括號和字符之間不出現空格;同樣,右小括號和字符之間也不出現空格;而左大括號前需要空格。
  3. if/for/while/switch/do 等保留字與括號之間都必須加空格
  4. 任何二目、三目運算符的左右兩邊都需要加一個空格。
  5. 採用 4 個空格縮進,禁止使用 tab 字符。
  6. 註釋的雙斜線與註釋內容之間有且僅有一個空格。
  7. 在進行類型強制轉換時,右括號與強制轉換值之間不需要任何空格隔開
  8. 單行字符數限制不超過 120 個,超出需要換行,換行時遵循如下原則:
    1)第二行相對第一行縮進 4 個空格,從第三行開始,不再繼續縮進。
    2)運算符與下文一起換行。
    3)方法調用的點符號與下文一起換行。
    4)方法調用中的多個參數需要換行時,在逗號後進行。
    5)在括號前不要換行
  9. 方法參數在定義和傳入時,多個參數逗號後邊必須加空格
  10. IDE 的 text file encoding 設置爲 UTF-8; IDE 中文件的換行符使用 Unix 格式,不要使用 Windows 格式。
  11. 除註釋之外的方法簽名、左右大括號、方法內代碼、空行、回車及任何不可見字符的總行數不超過80 行。
  12. 沒有必要增加若干空格來使變量的賦值等號與上一行對應位置的等號對齊。
  13. 不同邏輯、不同語義、不同業務的代碼之間插入一個空行分隔開來以提升可讀性

(四)OOP規約

  1. 避免通過一個類的對象引用訪問此類的靜態變量或靜態方法,無謂增加編譯器解析成本,直接用類名來訪問即可。
  2. 所有的覆寫方法,必須加@Override 註解
  3. 相同參數類型,相同業務含義,纔可以使用 Java 的可變參數,避免使用 Object。
  4. 外部正在調用或者二方庫依賴的接口,不允許修改方法簽名,避免對接口調用方產生影響。接口過時必須加@Deprecated 註解,並清晰地說明採用的新接口或者新服務是什麼。
  5. 不能使用過時的類或方法。
  6. Object 的 equals 方法容易拋空指針異常,應使用常量或確定有值的對象來調用
    equals。
  7. 所有整型包裝類對象之間值的比較,全部使用 equals 方法比較。對於 Integer var = ? 在-128 至 127 範圍內的賦值,Integer 對象是在 IntegerCache.cache 產
    生,會複用已有對象,這個區間內的 Integer 值可以直接使用==進行判斷,但是這個區間之外的所有數據,都會在堆上產生,並不會複用已有對象,這是一個大坑,推薦使用 equals 方法進行判斷。
  8. 浮點數之間的等值判斷,基本數據類型不能用==來比較,包裝數據類型不能用
    equals 來判斷。浮點數採用“尾數+階碼”的編碼方式,類似於科學計數法的“有效數字+指數”的表示方式。二進制無法精確表示大部分的十進制小數
  9. 定義數據對象 DO 類時,屬性類型要與數據庫字段類型相匹配
  10. 爲了防止精度損失,禁止使用構造方法 BigDecimal(double)的方式把 double 值轉化爲 BigDecimal 對象。
  11. 關於基本數據類型與包裝數據類型的使用標準如下:
    1) 所有的 POJO 類屬性必須使用包裝數據類型。
    2) RPC 方法的返回值和參數必須使用包裝數據類型。
    3) 所有的局部變量使用基本數據類型。
  12. 定義 DO/DTO/VO 等 POJO 類時,不要設定任何屬性默認值。
  13. 序列化類新增屬性時,請不要修改 serialVersionUID 字段,避免反序列失敗;如果完全不兼容升級,避免反序列化混亂,那麼請修改 serialVersionUID 值。
  14. 構造方法裏面禁止加入任何業務邏輯,如果有初始化邏輯,請放在 init 方法中
  15. POJO 類必須寫 toString 方法。使用 IDE 中的工具:source> generate toString時,如果繼承了另一個 POJO 類,注意在前面加一下 super.toString.
  16. 禁止在 POJO 類中,同時存在對應屬性 xxx 的 isXxx()和 getXxx()方法
  17. 使用索引訪問用 String 的 split 方法得到的數組時,需做最後一個分隔符後有無內容的檢查,否則會有拋IndexOutOfBoundsException 的風險。
  18. 當一個類有多個構造方法,或者多個同名方法,這些方法應該按順序放置在一起,便於閱讀,此條規則優先於下一條
  19. 類內方法定義的順序依次是:公有方法或保護方法 > 私有方法 > getter / setter方法。
  20. setter 方法中,參數名稱與類成員變量名稱一致,this.成員名 = 參數名。在getter/setter 方法中,不要增加業務邏輯,增加排查問題的難度
  21. 循環體內,字符串的連接方式,使用 StringBuilder 的 append 方法進行擴展。
  22. final 可以聲明類、成員變量、方法、以及本地變量,下列情況使用 final 關鍵字:
    1) 不允許被繼承的類,如:String 類。
    2) 不允許修改引用的域對象。
    3) 不允許被覆寫的方法,如:POJO 類的 setter 方法。
    4) 不允許運行過程中重新賦值的局部變量。
    5) 避免上下文重複使用一個變量,使用 final 可以強制重新定義一個變量,方便更好地進行重構。
  23. 慎用 Object 的 clone 方法來拷貝對象。
  24. 類成員與方法訪問控制從嚴:
    1) 如果不允許外部直接通過 new 來創建對象,那麼構造方法必須是 private。
    2) 工具類不允許有 public 或 default 構造方法。
    3) 類非 static 成員變量並且與子類共享,必須是 protected。
    4) 類非 static 成員變量並且僅在本類使用,必須是 private。
    5) 類 static 成員變量如果僅在本類使用,必須是 private。
    6) 若是 static 成員變量,考慮是否爲 final。
    7) 類成員方法只供類內部調用,必須是 private。
    8) 類成員方法只對繼承類公開,那麼限制爲 protected。

(五) 集合處理

  1. 【強制】關於 hashCode 和 equals 的處理,遵循如下規則:
    1) 只要覆寫 equals,就必須覆寫 hashCode。
    2) 因爲 Set 存儲的是不重複的對象,依據 hashCode 和 equals 進行判斷,所以 Set 存儲的對象必須覆寫這兩個方法。
    3) 如果自定義對象作爲 Map 的鍵,那麼必須覆寫 hashCode 和 equals。
  2. 【強制】ArrayList 的 subList 結果不可強轉成 ArrayList,否則會拋出 ClassCastException 異常,即 java.util.RandomAccessSubList cannot be cast to java.util.ArrayList。
  3. 使用 Map 的方法 keySet()/values()/entrySet()返回集合對象時,不可以對其進行添加元素操作,否則會拋出 UnsupportedOperationException 異常。
  4. . 【強制】Collections 類返回的對象,如:emptyList()/singletonList()等都是 immutable list,不可對其進行添加或者刪除元素的操作。
  5. . 【強制】在 subList 場景中,高度注意對原集合元素的增加或刪除,均會導致子列表的遍歷、增加、刪除產生 ConcurrentModificationException 異常。
  6. 使用集合轉數組的方法,必須使用集合的 toArray(T[] array),傳入的是類型完全一致、長度爲 0 的空數組。
  7. 在使用 Collection 接口任何實現類的 addAll()方法時,都要對輸入的集合參數進行NPE 判斷。
  8. 使用工具類 Arrays.asList()把數組轉換成集合時,不能使用其修改集合相關的方法,它的 add/remove/clear 方法會拋出 UnsupportedOperationException 異常。
  9. 泛型通配符<? extends T>來接收返回的數據,此寫法的泛型集合不能使用 add 方法,而<? super T>不能使用 get 方法,作爲接口調用賦值時易出錯。
  10. 在無泛型限制定義的集合賦值給泛型限制的集合時,在使用集合元素時,需要進行instanceof 判斷,避免拋出 ClassCastException 異常。
  11. 不要在 foreach 循環裏進行元素的 remove/add 操作。remove 元素請使用Iterator 方式,如果併發操作,需要對 Iterator 對象加鎖。
  12. 在 JDK7 版本及以上,Comparator 實現類要滿足如下三個條件,不然 Arrays.sort,Collections.sort 會拋 IllegalArgumentException 異常。
    三個條件如下
    1) x,y 的比較結果和 y,x 的比較結果相反。
    2) x>y,y>z,則 x>z。
    3) x=y,則 x,z 比較結果和 y,z 比較結果相同
  13. 集合泛型定義時,在 JDK7 及以上,使用 diamond 語法或全省略.
  14. 集合初始化時,指定集合初始值大小。
  15. 使用 entrySet 遍歷 Map 類集合 KV,而不是 keySet 方式進行遍歷。
  16. 高度注意 Map 類集合 K/V 能不能存儲 null 值的情況,如下表格:
集合類 Key Value Super 說明
Hashtable 不允許爲 null 不允許爲 null Dictionary 線程安全
ConcurrentHashMap 不允許爲 null 不允許爲null AbstractMap 鎖分段技術(JDK8:CAS)
TreeMap 不允許爲 null 允許爲 null AbstractMap 線程不安全
HashMap 允許爲 null 允許爲 null AbstractMap 線程不安全
  1. 合理利用好集合的有序性(sort)和穩定性(order),避免集合的無序性(unsort)和不穩
    定性(unorder)帶來的負面影響。

(六) 併發處理

  1. 獲取單例對象需要保證線程安全,其中的方法也要保證線程安全。
    說明:資源驅動類、工具類、單例工廠類都需要注意。
  2. 創建線程或線程池時請指定有意義的線程名稱,方便出錯時回溯。
  3. 線程資源必須通過線程池提供,不允許在應用中自行顯式創建線程。
  4. 線程池不允許使用 Executors 去創建,而是通過 ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。
  5. SimpleDateFormat 是線程不安全的類,一般不要定義爲 static 變量,如果定義爲static,必須加鎖,或者使用 DateUtils 工具類
  6. 必須回收自定義的 ThreadLocal 變量,尤其在線程池場景下,線程經常會被複用,如果不清理自定義的 ThreadLocal 變量,可能會影響後續業務邏輯和造成內存泄露等問題。儘量在代理中使用 try-finally 塊進行回收。
  7. 高併發時,同步調用應該去考量鎖的性能損耗。能用無鎖數據結構,就不要用鎖;能鎖區塊,就不要鎖整個方法體;能用對象鎖,就不要用類鎖。
  8. 對多個資源、數據庫表、對象同時加鎖時,需要保持一致的加鎖順序,否則可能會造成死鎖。
  9. 在使用阻塞等待獲取鎖的方式中,必須在 try 代碼塊之外,並且在加鎖方法與 try 代碼塊之間沒有任何可能拋出異常的方法調用,避免加鎖成功後,在 finally 中無法解鎖。
  10. 在使用嘗試機制來獲取鎖的方式中,進入業務代碼塊之前,必須先判斷當前線程是否持有鎖。鎖的釋放規則與鎖的阻塞等待方式相同。
  11. 併發修改同一記錄時,避免更新丟失,需要加鎖。要麼在應用層加鎖,要麼在緩存加鎖,要麼在數據庫層使用樂觀鎖,使用 version 作爲更新依據。
  12. 多線程並行處理定時任務時,Timer 運行多個 TimeTask 時,只要其中之一沒有捕獲拋出的異常,其它任務便會自動終止運行,如果在處理定時任務時使用
  13. 資金相關的金融敏感信息,使用悲觀鎖策略。
  14. 使用 CountDownLatch 進行異步轉同步操作,每個線程退出前必須調用 countDown方法,線程執行代碼注意 catch 異常,確保 countDown 方法被執行到,避免主線程無法執行至 await 方法,直到超時才返回結果。
  15. 避免 Random 實例被多線程使用,雖然共享該實例是線程安全的,但會因競爭同一seed 導致的性能下降。
  16. 在併發場景下,通過雙重檢查鎖(double-checked locking)實現延遲初始化的優化問題隱患(可參考 The “Double-Checked Locking is Broken” Declaration),推薦解決方案中較爲簡單一種(適用於 JDK5 及以上版本),將目標屬性聲明爲 volatile 型。
  17. volatile 解決多線程內存不可見問題。對於一寫多讀,是可以解決變量同步問題,但是如果多寫,同樣無法解決線程安全問題。
  18. HashMap 在容量不夠進行 resize 時由於高併發可能出現死鏈,導致 CPU 飆升,在開發過程中可以使用其它數據結構或加鎖來規避此風險。
  19. ThreadLocal 對象使用 static 修飾,ThreadLocal 無法解決共享對象的更新問題

(七) 控制語句

  1. 在一個 switch 塊內,每個 case 要麼通過 continue/break/return 等來終止,要麼註釋說明程序將繼續執行到哪一個 case 爲止;在一個 switch 塊內,都必須包含一個default 語句並且放在最後,即使它什麼代碼也沒有。
  2. 當 switch 括號內的變量類型爲 String 並且此變量爲外部參數時,必須先進行 null判斷。
  3. 在 if/else/for/while/do 語句中必須使用大括號
  4. 在高併發場景中,避免使用”等於”判斷作爲中斷或退出的條件。
  5. 表達異常的分支時,少用 if-else 方式
  6. 除常用方法(如 getXxx/isXxx)等外,不要在條件判斷中執行其它複雜的語句,將複雜邏輯判斷的結果賦值給一個有意義的布爾變量名,以提高可讀性。
  7. 不要在其它表達式(尤其是條件表達式)中,插入賦值語句
  8. 循環體中的語句要考量性能,以下操作儘量移至循環體外處理,如定義對象、變量、獲取數據庫連接,進行不必要的 try-catch 操作(這個 try-catch 是否可以移至循環體外)。
  9. 避免採用取反邏輯運算符
  10. 接口入參保護,這種場景常見的是用作批量操作的接口。
  11. 下列情形,需要進行參數校驗:
    1) 調用頻次低的方法。
    2) 執行時間開銷很大的方法。此情形中,參數校驗時間幾乎可以忽略不計,但如果因爲參數錯誤導致
    中間執行回退,或者錯誤,那得不償失。
    3) 需要極高穩定性和可用性的方法。
    4) 對外提供的開放接口,不管是 RPC/API/HTTP 接口。
    5) 敏感權限入口。
  12. 下列情形,不需要進行參數校驗:
    1) 極有可能被循環調用的方法。但在方法說明裏必須註明外部參數檢查要求。
    2) 底層調用頻度比較高的方法。畢竟是像純淨水過濾的最後一道,參數錯誤不太可能到底層纔會暴露
    問題。一般 DAO 層與 Service 層都在同一個應用中,部署在同一臺服務器中,所以 DAO 的參數校驗,可
    以省略。
    3) 被聲明成 private 只會被自己代碼所調用的方法,如果能夠確定調用方法的代碼傳入參數已經做過檢
    查或者肯定不會有問題,此時可以不校驗參數。

(八)註釋規約

  1. 【強制】類、類屬性、類方法的註釋必須使用 Javadoc 規範,使用/*內容/格式,不得使用// xxx 方式。
  2. 所有的抽象方法(包括接口中的方法)必須要用 Javadoc 註釋、除了返回值、參數、異常說明外,還必須指出該方法做什麼事情,實現什麼功能。
  3. 所有的類都必須添加創建者和創建日期。
  4. 方法內部單行註釋,在被註釋語句上方另起一行,使用//註釋。方法內部多行註釋使用/* */註釋,注意與代碼對齊。
  5. 所有的枚舉類型字段必須要有註釋,說明每個數據項的用途
  6. 與其“半吊子”英文來註釋,不如用中文註釋把問題說清楚。專有名詞與關鍵字保持英文原文即可。
  7. 代碼修改的同時,註釋也要進行相應的修改,尤其是參數、返回值、異常、核心邏輯等的修改。
  8. 謹慎註釋掉代碼。在上方詳細說明,而不是簡單地註釋掉。如果無用,則刪除。
  9. 對於註釋的要求:第一、能夠準確反映設計思想和代碼邏輯;第二、能夠描述業含義,使別的程序員能夠迅速瞭解到代碼背後的信息。完全沒有註釋的大段代碼對於閱讀形同天書,註釋是給自己看的,即使隔很長時間,也能清晰理解當時的思路;註釋也是給任者看的,使其能夠快速接替自己的工作。
  10. 好的命名、代碼結構是自解釋的,註釋力求精簡準確、表達到位。避免出現註釋一個極端:過多過濫的註釋,代碼的邏輯一旦修改,修改註釋是相當大的負擔
  11. 特殊註釋標記,請註明標記人與標記時間。注意及時處理這些標記,通過標記描,經常清理此類標記。線上故障有時候就是來源於這些標記處的代碼。

(九)其它

  1. 在使用正則表達式時,利用好其預編譯功能,可以有效加快正則匹配速度
  2. velocity 調用 POJO 類的屬性時,直接使用屬性名取值即可,模板引擎會自動按規調用 POJO 的 getXxx(),如果是boolean 基本數據類型變量(boolean 命名不需要加 is 綴),會自動調用 isXxx()方法
  3. 後臺輸送給頁面的變量必須加$!{var}——中間的感嘆號。
  4. 注意 Math.random() 這個方法返回是 double 類型,注意取值的範圍 0≤x<1(能取到零值,注意除零異常),如果想獲取整數類型的隨機數,不要將 x 放大 10 的若干倍然取整,直接使用 Random 對象的 nextInt 或者 nextLong 方法。
  5. 獲取當前毫秒數 System.currentTimeMillis(); 而不是 new Date().getTime();
  6. 日期格式化時,傳入 pattern 中表示年份統一使用小寫的 y。
表示日期和時間的格式如下所示:
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  1. 不要在視圖模板中加入任何複雜的邏輯。根據 MVC 理論,視圖的職責是展示,不要搶模型和控制器的活
  2. 任何數據結構的構造或初始化,都應指定大小,避免數據結構無限增長喫光內存。
  3. 及時清理不再使用的代碼段或配置信息。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章