OOP規約 集合處理

我的博客網站:http://www.caoyong.xin:8080/blogger(歡迎訪問!有我更多的文章)


四:OOP 規約

 

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

 

2. 【強制】所有的覆寫方法,必須加@Override 註解。

 

說明:getObject() get0bject()的問題。一個是字母的 O,一個是數字的 0,加@Override

 

可以準確判斷是否覆蓋成功。另外,如果在抽象類中對方法簽名進行修改,其實現類會馬上編譯報錯。

 

3. 【強制】相同參數類型,相同業務含義,纔可以使用 Java 的可變參數,避免使用 Object說明:可變參數必須放置在參數列表的最後。(提倡同學們儘量不用可變參數編程)

 

正例:public User getUsers(String type, Integer... ids) {...}

 

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

 

5. 【強制】不能使用過時的類或方法。

 

說明:java.net.URLDecoder 中的方法 decode(String encodeStr) 這個方法已經過時,應該使用雙參數 decode(String source, String encode)。接口提供方既然明確是過時接口,那麼有義務同時提供新的接口;作爲調用方來說,有義務去考證過時方法的新實現是什麼。

 

6. 【強制】Object  equals 方法容易拋空指針異常,應使用常量或確定有值的對象來調用

 

equals

 

正例:"test".equals(object);

 

反例:object.equals("test");

 

說明:推薦使用 java.util.Objects#equalsJDK7 引入的工具類)


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

 

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

 

1 【強制】所有的 POJO 類屬性必須使用包裝數據類型。

 

2 【強制】RPC 方法的返回值和參數必須使用包裝數據類型。

 

3 【推薦】所有的局部變量使用基本數據類型。

 

說明:POJO 類屬性沒有初值是提醒使用者在需要使用時,必須自己顯式地進行賦值,任何

 

NPE 問題,或者入庫檢查,都由使用者來保證。

 

正例:數據庫的查詢結果可能是 null,因爲自動拆箱,用基本數據類型接收有 NPE 風險。

 

反例:比如顯示成交總額漲跌情況,即正負 x%爲基本數據類型,調用的 RPC 服務,調用不成功時,返回的是默認值,頁面顯示爲 0%,這是不合理的,應該顯示成中劃線。所以包裝

 

數據類型的 null 值,能夠表示額外的信息,如:遠程調用失敗,異常退出。

 

9. 【強制】定義 DO/DTO/VO  POJO 類時,不要設定任何屬性默認值

 

反例:POJO 類的 gmtCreate 默認值爲 new Date();但是這個屬性在數據提取時並沒有置入具體值,在更新其它字段時又附帶更新了此字段,導致創建時間被修改成當前時間。

 

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

 

說明:注意 serialVersionUID 不一致會拋出序列化運行時異常。

 

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

 

12. 【強制】POJO 類必須寫 toString 方法。使用 IDE 中的工具:source> generate toString

 

時,如果繼承了另一個 POJO 類,注意在前面加一下 super.toString

 

說明:在方法執行拋出異常時,可以直接調用 POJO  toString()方法打印其屬性值,便於排查問題。

 

13. 【推薦】使用索引訪問用 String  split 方法得到的數組時,需做最後一個分隔符後有無內容的檢查,否則會有拋 IndexOutOfBoundsException 的風險。

 

說明:

 

String str = "a,b,c,,";

 

String[] ary = str.split(",");

 

預期大於 3,結果是 3 System.out.println(ary.length);

 

13. 【推薦】當一個類有多個構造方法,或者多個同名方法,這些方法應該按順序放置在一起,便於閱讀,此條規則優先於第 15 條規則。


 

15. 【推薦】 類內方法定義的順序依次是:公有方法或保護方法 > 私有方法 > getter/setter

 

方法。

 

說明:公有方法是類的調用者和維護者最關心的方法,首屏展示最好;保護方法雖然只是子類關心,也可能是模板設計模式下的核心方法;而私有方法外部一般不需要特別關心,是一個黑盒實現;因爲承載的信息價值較低,所有 Service  DAO  getter/setter 方法放在類體

 

最後。

 

16. 【推薦】setter 方法中,參數名稱與類成員變量名稱一致,this.成員名 = 參數名。在 getter/setter 方法中,不要增加業務邏輯,增加排查問題的難度。

 

反例:

 

public Integer getData() {

 

if (condition) {

 

return this.data + 100;

 

} else {

 

return this.data - 100;

 

}

 

}

 

17. 【推薦】循環體內,字符串的連接方式,使用 StringBuilder  append 方法進行擴展。說明:反編譯出的字節碼文件顯示每次循環都會 new 出一個 StringBuilder 對象,然後進行 append 操作,最後通過 toString 方法返回 String 對象,造成內存資源浪費。

 

反例:

 

String str = "start";

 

for (int i = 0; i < 100; i++) {

 

str = str + "hello";

 

}

 

18. 【推薦】final 可以聲明類、成員變量、方法、以及本地變量,下列情況使用 final 關鍵字:1 不允許被繼承的類,如:String 類。

 

2 不允許修改引用的域對象,如:POJO 類的域變量。

 

3 不允許被重寫的方法,如:POJO 類的 setter 方法。

 

4 不允許運行過程中重新賦值的局部變量。

 

5 避免上下文重複使用一個變量,使用 final 描述可以強制重新定義一個變量,方便更好

 

地進行重構。

 

19. 【推薦】慎用 Object  clone 方法來拷貝對象。

 

說明:對象的 clone 方法默認是淺拷貝,若想實現深拷貝需要重寫 clone 方法實現屬性對象

 

的拷貝。

 

20. 【推薦】類成員與方法訪問控制從嚴:

 

     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 成員方法或

 

成員變量,刪除一下,不得手心冒點汗嗎?變量像自己的小孩,儘量在自己的視線內,變量作

 

用域太大,無限制的到處跑,那麼你會擔心的。

 

 

 

 

五:集合處理

 

1. 【強制】關於 hashCode  equals 的處理,遵循如下規則:

 

1 只要重寫 equals,就必須重寫 hashCode

 

2 因爲 Set 存儲的是不重複的對象,依據 hashCode  equals 進行判斷,所以 Set 存儲的對象必須重寫這兩個方法。

 

3 如果自定義對象作爲 Map 的鍵,那麼必須重寫 hashCode  equals

 

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

 

2. 【強制】ArrayList  subList 結果不可強轉成 ArrayList,否則會拋出 ClassCastException 異常,即 java.util.RandomAccessSubList cannot be cast to java.util.ArrayList. 說明:subList 返回的是 ArrayList 的內部類 SubList,並不是 ArrayList ,而是

 

ArrayList 的一個視圖,對於 SubList 子列表的所有操作最終會反映到原列表上。

 

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

 

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

 

說明:使用 toArray 帶參方法,入參分配的數組空間不夠大時,toArray 方法內部將重新分配內存空間,並返回新數組地址;如果數組元素個數大於實際所需,下標爲[ list.size() ] 的數組元素將被置爲 null,其它數組元素保持原值,因此最好將方法入參數組大小定義與集合元素個數一致。

 

正例:

 

List<String> list = new ArrayList<String>(2);

 

list.add("guan");

 

list.add("bao");

 

String[] array = new String[list.size()];

 

array = list.toArray(array);

 

反例:直接使用 toArray 無參方法存在問題,此方法返回值只能是 Object[]類,若強轉其它

 

類型數組將出現 ClassCastException 錯誤。

 

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

 

6. 【強制】泛型通配符<? extends T>來接收返回的數據,此寫法的泛型集合不能使用 add 方法,而<? super T>不能使用 get 方法,作爲接口調用賦值時易出錯。

 

說明:擴展說一下 PECS(Producer Extends Consumer Super)原則:第一、頻繁往外讀取內容的,適合用<? extends T>。第二、經常往裏插入的,適合用<? super T>

 

7. 【強制】不要在 foreach 循環裏進行元素的 remove/add 操作。remove 元素請使用 Iterator

 

方式,如果併發操作,需要對 Iterator 對象加鎖。

 

正例:

 

Iterator<String> iterator = list.iterator();

 

while (iterator.hasNext()) {

 

String item = iterator.next();

 

if (刪除元素的條件) {

 

iterator.remove();

 

}

 

}

 

反例:

 

List<String> list = new ArrayList<String>();

 

list.add("1");

 

list.add("2");

 

for (String item : list) {

 

if ("1".equals(item)) {

 

list.remove(item);

 

}

 

}

 

說明:以上代碼的執行結果肯定會出乎大家的意料,那麼試一下把“1”換成“2”,會是同樣的

 

結果嗎?

 

8. 【強制】  JDK7 版本及以上,Comparator 要滿足如下三個條件,不然 Arrays.sort

 

Collections.sort 會報 IllegalArgumentException 異常。

 

說明:三個條件如下

 

1 xy 的比較結果和 yx 的比較結果相反。

 

2 x>yy>z,則 x>z

 

 

3 x=y,則 xz 比較結果和 yz 比較結果相同。

 

反例:下例中沒有處理相等的情況,實際使用中可能會出現異常:

 

new Comparator<Student>() {

 

@Override

 

public int compare(Student o1, Student o2) {

 

return o1.getId() > o2.getId() ? 1 : -1;

 

}

 

};

 

9. 【推薦】集合初始化時,指定集合初始值大小。

 

說明:HashMap 使用 HashMap(int initialCapacity) 初始化,

 

正例:initialCapacity = (需要存儲的元素個數 / 負載因子) + 1。注意負載因子(即 loader factor)默認爲 0.75,如果暫時無法確定初始值大小,請設置爲 16(即默認值)。

 

反例:HashMap 需要放置 1024 個元素,由於沒有設置容量初始大小,隨着元素不斷增加,容

 

量 7 次被迫擴大,resize 需要重建 hash 表,嚴重影響性能。

 

10. 【推薦】使用 entrySet 遍歷 Map 類集合 KV,而不是 keySet 方式進行遍歷。

 

說明:keySet 其實是遍歷了 2 次,一次是轉爲 Iterator 對象,另一次是從 hashMap 中取出

 

key 所對應的 value。而 entrySet 只是遍歷了一次就把 key  value 都放到了 entry 中,效

 

率更高。如果是 JDK8,使用 Map.foreach 方法。

 

正例:values()返回的是 V 值集合,是一個 list 集合對象;keySet()返回的是 K 值集合,是一個 Set 集合對象;entrySet()返回的是 K-值組合集合。

 

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

 

集合類

Key

Value

Super

說明

     

Hashtable

不允許爲 null

不允許爲 null

Dictionary

線程安全

     

ConcurrentHashMap

不允許爲 null

不允許爲 null

AbstractMap

鎖分段技術(JDK8:CAS

     

TreeMap

不允許爲 null

允許爲 null

AbstractMap

線程不安全

     

HashMap

允許爲 null

允許爲 null

AbstractMap

線程不安全

     

 

反例: 由於 HashMap 的干擾,很多人認爲 ConcurrentHashMap 是可以置入 null 值,而事實上,

 

存儲 null 值時會拋出 NPE 異常。

 

12. 【參考】合理利用好集合的有序性(sort)和穩定性(order),避免集合的無序性(unsort)和不穩定性(unorder)帶來的負面影響。

 

說明:有序性是指遍歷的結果是按某種比較規則依次排列的。穩定性指集合每次遍歷的元素次序是一定的。如:ArrayList  order/unsortHashMap  unorder/unsortTreeSet order/sort

     

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章