1. 【強制】避免通過一個類的對象引用通路此類的靜态變量或靜态方法,無謂增加編譯器解析成本,直接用類名來通路即可。
2. 【強制】 所有的覆寫方法,必須加@ Override 注解。
說明: getObject()與 get0bject()的問題。一個是字母的 O,一個是數字的 0,加@Override 可以準确判斷是否覆寫成功。另外,如果在抽象類中對方法簽名進行修改,其實作類會馬上編譯報錯。
3. 【強制】 相同參數類型,相同業務含義,才可以使用 Java 的可變參數,避免使用 Object 。
說明: 可變參數必須放置在參數清單的最後。(盡量不用可變參數)
正例: public List<User> listUsers(String type, Long... 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#equals(JDK7 引入的工具類)。
7. 【強制】 所有整型包裝類對象之間 值的比較, 全部使用 equals 方法比較。
說明: 對于 Integer var = ? 在 -128 至 127 之間的指派,Integer 對象是在 IntegerCache.cache 産生,會複用已有對象,這個區間内的 Integer 值可以直接使用==進行判斷,但是這個區間之外的所有資料,都會在堆上産生,并不會複用已有對象,這是一個大坑,推薦使用 equals 方法進行判斷。
8. 【強制】 任何貨币金額,均以最小貨币機關且整型類型來進行存儲。
9. 【強制】 浮點數之間的等值判斷,基本資料類型不能用==來比較,包裝資料類型不能用 equals 來判斷。
說明: 浮點數采用“尾數+階碼”的編碼方式,類似于科學計數法的“有效數字+指數”的表示方式。二進
制無法精确表示大部分的十進制小數,具體原理參考 《碼出高效》 。
反例:
float a = 1.0f - 0.9f ;
float b = 0.9f - 0.8f ;
if ( a == b ) {
// 預期進入此代碼快,執行其它業務邏輯
// 但事實上 a==b 的結果為 false
}
Float x = Float . valueOf ( a );
Float y = Float . valueOf ( b );
if ( x . equals ( y )) {
// 但事實上 equals 的結果為 false
正例:
(1) 指定一個誤差範圍,兩個浮點數的內插補點在此範圍之内,則認為是相等的。
float diff = 1e-6f ;
if ( Math . abs ( a - b ) < diff ) {
System . out . println ( "true" );
(2) 使用 BigDecimal 來定義值,再進行浮點數的運算操作。
BigDecimal a = new BigDecimal ( "1.0" );
BigDecimal b = new BigDecimal ( "0.9" );
BigDecimal c = new BigDecimal ( "0.8" );
BigDecimal x = a . subtract ( b );
BigDecimal y = b . subtract ( c );
10. 【強制】 定義資料對象 DO 類時,屬性類型要與資料庫字段類型相比對。
正例: 資料庫字段的 bigint 必須與類屬性的 Long 類型相對應。
反例: 某個案例的資料庫表 id 字段定義類型 bigint unsigned,實際類對象屬性為 Integer,随着 id 越來
越大,超過 Integer 的表示範圍而溢出成為負數。
11. 【強制】 禁止使用構造方法 BigDecimal(double) 的方式把 double 值 轉化為 BigDecimal 對象 。
說明: BigDecimal(double)存在精度損失風險,在精确計算或值比較的場景中可能會導緻業務邏輯異常。
如:BigDecimal g = new BigDecimal(0.1f); 實際的存儲值為:0.10000000149
正例: 優先推薦入參為 String 的構造方法,或使用 BigDecimal 的 valueOf 方法,此方法内部其實執行了
Double 的 toString,而 Double 的 toString 按 double 的實際能表達的精度對尾數進行了截斷。
BigDecimal recommend1 = new BigDecimal ( "0.1" );
BigDecimal recommend2 = BigDecimal . valueOf ( 0.1 );
12.關于基本資料類型與包裝資料類型的使用标準如下:
1) 【強制】 所有的 POJO 類屬性必須使用包裝資料類型。
2) 【強制】 RPC 方法的傳回值和參數必須使用包裝資料類型。
3) 【推薦】 所有的局部變量使用基本資料類型。
說明: POJO 類屬性沒有初值是提醒使用者在需要使用時,必須自己顯式地進行指派,任何 NPE 問題,或
者入庫檢查,都由使用者來保證。
正例: 資料庫的查詢結果可能是 null,因為自動拆箱,用基本資料類型接收有 NPE 風險。
反例: 某業務的交易報表上顯示成交總額漲跌情況,即正負 x%,x 為基本資料類型,調用的 RPC 服務,調
用不成功時,傳回的是預設值,頁面顯示為 0%,這是不合理的,應該顯示成中劃線-。是以包裝資料類型
的 null 值,能夠表示額外的資訊,如:遠端調用失敗,異常退出。
13. 【強制】 定義 DO/DTO/VO 等 POJO 類時,不要設定任何屬性 預設值 。
反例: POJO 類的 createTime 預設值為 new Date(),但是這個屬性在資料提取時并沒有置入具體值,在
更新其它字段時又附帶更新了此字段,導緻建立時間被修改成目前時間。
14. 【強制】 序列化類新增屬性時,請不要修改 serialVersionUID 字段,避免反序列失敗;如果完全不相容更新,避免反序列化混亂,那麼請修改 serialVersionUID 值。
說明: 注意 serialVersionUID 不一緻會抛出序列化運作時異常。
15. 【強制】 構造方法裡面禁止加入任何業務邏輯,如果有初始化邏輯,請放在 init 方法中。
16. 【強制】 POJO 類必須寫 toString 方法。使用 IDE 中的工具:source> generate toString 時,如果繼承了另一個 POJO 類,注意在前面加一下 super.toString。
說明: 在方法執行抛出異常時,可以直接調用 POJO 的 toString()方法列印其屬性值,便于排查問題。
17. 【強制】 禁止在 POJO 類中,同時存在對應屬性 xxx 的 isXxx() 和 getXxx() 方法。
說明: 架構在調用屬性 xxx 的提取方法時,并不能确定哪個方法一定是被優先調用到,神坑之一。
18. 【推薦】 使用索引通路用 String 的 split 方法得到的數組時,需做最後一個分隔符後有無内容
的檢查,否則會有抛 IndexOutOfBoundsException 的風險。
說明:
String str = "a,b,c,," ;
String [] ary = str . split ( "," );
// 預期大于 3,結果是 3
System . out . println ( ary . length );
19. 【推薦】 當一個類有多個構造方法,或者多個同名方法,這些方法應該按順序放置在一起,便
于閱讀,此條規則優先于下一條。
20. 【推薦】 類内方法定義的順序依次是:公有方法或保護方法 > 私有方法 > getter / setter
方法。
說明: 公有方法是類的調用者和維護者最關心的方法,首屏展示最好;保護方法雖然隻是子類關心,也可
能是“模闆設計模式”下的核心方法;而私有方法外部一般不需要特别關心,是一個黑盒實作;因為承載
的資訊價值較低,所有 Service 和 DAO 的 getter/setter 方法放在類體最後。
21. 【推薦】 setter 方法中,參數名稱與類成員變量名稱一緻,this.成員名 = 參數名。在
getter/setter 方法中,不要增加業務邏輯,增加排查問題的難度。
public Integer getData () {
if ( condition ) {
return this . data + 100 ;
} else {
return this . data - 100 ;
22. 【推薦】 循環體内,字元串的連接配接方式,使用 StringBuilder 的 append 方法進行擴充。
說明: 下例中,反編譯出的位元組碼檔案顯示每次循環都會 new 出一個 StringBuilder 對象,然後進行 append
操作,最後通過 toString 方法傳回 String 對象,造成記憶體資源浪費。
String str = "start" ;
for ( int i = 0 ; i < 100 ; i ++ ) {
str = str + "hello" ;
23. 【推薦】 final 可以聲明類、成員變量、方法、以及本地變量,下列情況使用 final 關鍵字:
1) 不允許被繼承的類,如:String 類。
2) 不允許修改引用的域對象,如:POJO 類的域變量。
3) 不允許被覆寫的方法,如:POJO 類的 setter 方法。
4) 不允許運作過程中重新指派的局部變量。
5) 避免上下文重複使用一個變量,使用 final 可以強制重新定義一個變量,友善更好地進行重構。
24. 【推薦】 慎用 Object 的 clone 方法來拷貝對象。
說明: 對象 clone 方法預設是淺拷貝,若想實作深拷貝需覆寫 clone 方法實作域對象的深度周遊式拷貝。
25. 【推薦】 類成員與方法通路控制從嚴:
1) 如果不允許外部直接通過 new 來建立對象,那麼構造方法必須是 private。
2) 工具類不允許有 public 或 default 構造方法。
3) 類非 static 成員變量并且與子類共享,必須是 protected。
4) 類非 static 成員變量并且僅在本類使用,必須是 private。
5) 類 static 成員變量如果僅在本類使用,必須是 private。
6) 若是 static 成員變量,考慮是否為 final。
7) 類成員方法隻供類内部調用,必須是 private。
8) 類成員方法隻對繼承類公開,那麼限制為 protected。
說明: 任何類、方法、參數、變量,嚴控通路範圍。過于寬泛的通路範圍,不利于子產品解耦。