1. 程式人生 > 實用技巧 >雙人專案 徐銘宇 吳磊 程式碼規範

雙人專案 徐銘宇 吳磊 程式碼規範

讀《阿里巴巴Java開發手冊華山版》,結合軟體工程導論課程所學內容整理

命名規約

【強制】 POJO 類中布林型別的變數,都不要加 is ,否則部分框架解析會引起序列化錯誤。
反例:定義為基本資料型別 boolean isSuccess;的屬性,它的方法也是 isSuccess() ,RPC框架在反向解析的時候,“以為”對應的屬性名稱是 success ,導致屬性獲取不到,進而丟擲異常。

對於isSuccess這個布林變數,IDE在自動生成getter,setter方法時,生成的方法名稱是isSuccess和setSuccess,而不是isIsSuccess和setIsSuccess,

public class DemoPOJO{
    boolean active;
    boolean isSuccess;

    public boolean isActive() {
        return active;
    }

    public void setActive(boolean active) {
        this.active = active;
    }

    public boolean isSuccess() {
        return isSuccess;
    }

    public void setSuccess(boolean success) {
        isSuccess = success;
    }
 }

除了RPC框架反向解析會有問題,型別情況反向解析時也會有問題:
比如SpringMVC在接收前端頁面傳回一個"isSuccess"布林變數時,解析成為POJO物件時,找不到setIsSuccess方法,導致POJO的屬性不能正確獲取,而且比較坑的是,這種情況不容易發現異常,最終解析後的屬性值是拿到布林型別預設值false。

常量定義

【推薦】不要使用一個常量類維護所有常量,應該按常量功能進行歸類,分開維護。如:快取相關的常量放在類: CacheConsts 下 ; 系統配置相關的常量放在類: ConfigConsts 下。
說明:大而全的常量類,非得使用查詢功能才能定位到修改的常量,不利於理解和維護。

在寫程式碼的時候,從易用性和可維護性出發,不推薦一個類內容太多,大而全的類,改起來牽一髮動全身,一個類只負責一類功能,不要涵蓋太多方面。

OOP規約

【強制】所有的覆寫方法,必須加@ Override 註解。
反例: getObject() 與 get 0 bject() 的問題。一個是字母的 O ,一個是數字的 0,加@ Override可以準確判斷是否覆蓋成功。另外,如果在抽象類中對方法簽名進行修改,其實現類會馬上編譯報錯。

儘量用最安全的方式寫程式碼,儘量讓問題在編譯期暴露,而不是執行期暴露

【強制】 Object 的 equals 方法容易拋空指標異常,應使用常量或確定有值的物件來呼叫equals 。
正例:" test " .equals(object);
反例: object.equals( " test " );
說明:推薦使用 java . util . Objects # equals (JDK 7 引入的工具類 )

類似的還有使用 ""符號的時候,寫成:if(100 == sum)比起寫成:if(sum100),前者更好,因為這樣可以避免不小心寫成if(sum=100)的問題,前者會編譯報錯

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

 Integer integerA1 = 100;
 Integer integerA2 = 100;
 System.out.println(integerA1 == integerA2);  //true

 Integer integerB1 = 1000;
 Integer integerB2 = 1000;
 System.out.println(integerB1 == integerB2);  //false

 System.out.println(integerB1.equals(1000));  //true

【強制】關於基本資料型別與包裝資料型別的使用標準如下:
1 ) 所有的 POJO 類屬性必須使用包裝資料型別。
2 ) RPC 方法的返回值和引數必須使用包裝資料型別。
3 ) 所有的區域性變數【推薦】使用基本資料型別。
說明: POJO 類屬性沒有初值是提醒使用者在需要使用時,必須自己顯式地進行賦值,任何NPE 問題,或者入庫檢查,都由使用者來保證。
正例:資料庫的查詢結果可能是 null ,因為自動拆箱,用基本資料型別接收有 NPE 風險。
反例:比如顯示成交總額漲跌情況,即正負 x %, x 為基本資料型別,呼叫的 RPC 服務,呼叫不成功時,返回的是預設值,頁面顯示:0%,這是不合理的,應該顯示成中劃線-。所以包裝資料型別的 null 值,能夠表示額外的資訊,如:遠端呼叫失敗,異常退出。

NPE問題:空指標異常(Null Pointer Exception)

【強制】定義 DO / DTO / VO 等 POJO 類時,不要設定任何屬性預設值。
反例: POJO 類的 createTime 預設值為 new Date(); 但是這個屬性在資料提取時並沒有置入具體值,在更新其它欄位時又附帶更新了此欄位,導致建立時間被修改成當前時間。

【強制】 POJO 類必須寫 toString 方法。使用 IDE 的中工具: source > generate ,toString時,如果繼承了另一個 POJO 類,注意在前面加一下 super.toString 。
說明:在方法執行丟擲異常時,可以直接呼叫 POJO 的 toString() 方法列印其屬性值,便於排查問題。

【推薦】 類內方法定義順序依次是:公有方法或保護方法 > 私有方法 > getter / setter方法。
說明:公有方法是類的呼叫者和維護者最關心的方法,首屏展示最好;保護方法雖然只是子類關心,也可能是“模板設計模式”下的核心方法;而私有方法外部一般不需要特別關心,是一個黑盒實現;因為方法資訊價值較低,所有 Service 和 DAO 的 getter / setter 方法放在類體最後。

【推薦】類成員與方法訪問控制從嚴:
1 ) 如果不允許外部直接通過 new 來建立物件,那麼構造方法必須是 private 。
2 ) 工具類不允許有 public 或 default 構造方法。
3 ) 類非 static 成員變數並且與子類共享,必須是 protected 。
4 ) 類非 static 成員變數並且僅在本類使用,必須是 private 。
5 ) 類 static 成員變數如果僅在本類使用,必須是 private 。
6 ) 若是 static 成員變數,必須考慮是否為 final 。
7 ) 類成員方法只供類內部呼叫,必須是 private 。
8 ) 類成員方法只對繼承類公開,那麼限制為 protected 。
說明:任何類、方法、引數、變數,嚴控訪問範圍。過寬泛的訪問範圍,不利於模組解耦。思考:如果是一個 private 的方法,想刪除就刪除,可是一個 public 的 Service 方法,或者一個 public 的成員變數,刪除一下,不得手心冒點汗嗎?變數像自己的小孩,儘量在自己的視線內,變數作用域太大,如果無限制的到處跑,那麼你會擔心的。

1)將構造方法私有化,一般在單例模式下用得比較多,這時使用getInstance()方法來獲取一個例項物件。
2)工具類的話,應該暴露出來的方法是靜態方法,使用者靜態呼叫,不必例項化物件。
嚴格控制訪問範圍,也可以避免屬性值被不小心亂改。

集合處理

【強制】關於 hashCode 和 equals 的處理,遵循如下規則:
1) 只要重寫 equals ,就必須重寫 hashCode 。
2) 因為 Set 儲存的是不重複的物件,依據 hashCode 和 equals 進行判斷,所以 Set 儲存的物件必須重寫這兩個方法。
3) 如果自定義物件做為 Map 的鍵,那麼必須重寫 hashCode 和 equals 。
正例: String 重寫了 hashCode 和 equals 方法,所以我們可以非常愉快地使用 String 物件作為 key 來使用。

假如類User重寫了“equals”,沒有重寫“hashCode”方法,現在有userA和userB 2個物件,它們用equals比較時為true,2個物件存入一個Set集合中,Set呼叫User類預設的hashCode方法,結果在集合中就儲存了2個User物件而不是我們想象中的一個 User物件

關於快速重寫hashCode,有許多方法:

  • Google的Guava專案裡有處理hashCode()和equals()的工具類
  • com.google.common.base.ObjectsApache Commons也有類似的工具類EqualsBuilder和HashCodeBuilder
  • Java 7 也提供了工具類java.util.Objects
  • 常用IDE都提供hashCode()和equals()的程式碼生成。

【強制】 ArrayList 的 subList 結果不可強轉成 ArrayList , 否則會丟擲 ClassCastException異常: java . util . RandomAccessSubList cannot be cast to java . util . ArrayList ;
說明: subList 返回的是 ArrayList 的內部類 SubList ,並不是 ArrayList ,而是ArrayList 的一個檢視,對於 SubList 子列表的所有操作最終會反映到原列表上。

【強制】 在 subList 場景中,高度注意對原集合元素個數的修改,會導致子列表的遍歷、增加、刪除均產生 ConcurrentModificationException 異常。

【強制】使用工具類 Arrays . asList() 把陣列轉換成集合時,不能使用其修改集合相關的方法,它的 add / remove / clear 方法會丟擲UnsupportedOperationException 異常。
說明: asList 的返回物件是一個 Arrays 內部類,並沒有實現集合的修改方法。 Arrays . asList體現的是介面卡模式,只是轉換介面,後臺的資料仍是陣列。
String[] str = new String[] { "a", "b" };
List list = Arrays.asList(str);
第一種情況: list.add("c"); 執行時異常。
第二種情況: str[0]= "gujin"; 那麼 list.get(0) 也會隨之修改

類似問題,可以使用FindBugs外掛,自動掃描程式碼中的Bug,這類問題這個外掛是可以檢測出來的,類似的在物件中返回屬性域的一個引用時,對該引用的修改會影響原物件的域的。

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

集合類 Key Value Super 說明
Hashtable 不允許為null 不允許 null Dictionary 執行緒安全
ConcurrentHashMap 不允許為 null 不允許為 null AbstractMap 分段鎖技術
TreeMap 不允許為null 允許為 null AbstractMap 執行緒不安全
HashMap 允許為 null 允許為 null AbstractMap 執行緒不安全

反例: 由於 HashMap 的干擾,很多人認為 ConcurrentHashMap 是可以置入 null 值,注意儲存null 值時會丟擲 NPE 異常。

【參考】利用 Set 元素唯一的特性,可以快速對一個集合進行去重操作,避免使用 List的contains 方法進行遍歷、對比、去重操作。