程式碼不規範,這些問題你一定遇到過
統計了內部一個準備開源的Java專案不規範的程式碼數量及種類,資料比較敏感,不便公開。但是最經常出現的不規範型別可以說一下。
早先自己也去分享過程式碼規範,試著猜過哪些程式碼規範問題可能會犯,但靠猜不能解決問題,用內部的專案外掛掃了一下,統計所有出現程式碼問題如下。
程式碼規範問題
命名
最常見的問題,程式碼規範中接近大半的命名問題都有犯。
1 包名應該全部小寫
【強制】包名統一使用小寫,點分隔符之間有且僅有一個自然語義的英語單詞。包名統一使用 單數形式,但是類名如果有複數含義,類名可以使用複數形式。 正例:應用工具類包名為 com.alibaba.ai.util、類名為 MessageUtils(此規則參考 spring 的框架結構)
2 命名不能以_或者$開頭
程式碼中的命名均不能以下劃線或美元符號開始,也不能以下劃線或美元符號結束。
反例:__name / $name
3 類名使用UpperCamelCase風格
【強制】類名使用 UpperCamelCase 風格,但以下情形例外:DO / BO / DTO / VO / AO /
PO / UID 等。
正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion
反例:macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion
4 方法名沒有使用lowerCamelCase風格
【強制】方法名、引數名、成員變數、區域性變數都統一使用 lowerCamelCase 風格,必須遵從 駝峰形式。 正例: localValue / getHttpMessage() / inputUserId
5 常量命名應該全部大寫並且以下劃線分隔
【強制】常量命名全部大寫,單詞間用下劃線隔開,力求語義表達完整清楚,不要嫌名字長。
正例:MAX_STOCK_COUNT 反例:MAX_COUNT
6 抽象類名應該以Abstract或者Base開頭
【強制】抽象類命名使用 Abstract 或 Base 開頭;異常類命名使用 Exception 結尾;測試類
命名以它要測試的類的名稱開始,以 Test 結尾。
7 POJO布林型別變數,不要加is字首
【強制】POJO 類中布林型別的變數,都不要加 is 字首,否則部分框架解析會引起序列化錯誤。 反例:定義為基本資料型別 Boolean isDeleted 的屬性,它的方法也是 isDeleted(),RPC框架在反向解析的時候,“誤以為”對應的屬性名稱是 deleted,導致屬性獲取不到,進而拋 出異常。
常量定義
1 魔法值
【強制】不允許任何魔法值(即未經預先定義的常量)直接出現在程式碼中。
反例:
String key = "Id#taobao_" + tradeId;
cache.put(key,value);
2 Long型別的值,要以大寫的L結尾
【強制】在 long 或者 Long 賦值時,數值後使用大寫的 L,不能是小寫的 l,小寫容易跟數字 1 混淆,造成誤解。 說明:Long a = 2l; 寫的是數字的 21,還是 Long 型的 2?
3 不要一個類維護所有的常量
【推薦】不要使用一個常量類維護所有常量,要按常量功能進行歸類,分開維護。 說明:大而全的常量類,雜亂無章,使用查詢功能才能定位到修改的常量,不利於理解和維護。
正例:快取相關常量放在類 CacheConsts 下;系統配置相關常量放在類 ConfigConsts 下。
4 應該使用常量或者確認值的內容來呼叫equals
【強制】Object 的 equals 方法容易拋空指標異常,應使用常量或確定有值的物件來呼叫 equals。
正例:"test".equals(object);
反例:object.equals("test");
說明:推薦使用 java.util.Objects#equals(JDK7 引入的工具類)
程式碼格式
1 程式碼行數太長
【推薦】單個方法的總行數不超過80行。有效程式碼行數40行左右
說明:包括方法簽名、結束右大括號、方法內程式碼、註釋、空行、回車及任何不可見字元的總行數不超過80
行。
正例:程式碼邏輯分清紅花和綠葉,個性和共性,綠葉邏輯單獨出來成為額外方法,使主幹程式碼 更加清晰;共性邏輯抽取成為共性方法,便於複用和維護。
2 檔案編碼不統一
【強制】IDE 的 text file encoding 設定為 UTF-8; IDE 中檔案的換行符使用 Unix 格式,不要使用 Windows 格式。
集合處理
1 初始化HashMap等集合時,儘量指定初始值大小
【推薦】集合初始化時,指定集合初始值大小。 說明:HashMap 使用 HashMap(int initialCapacity) 初始化。
正例:initialCapacity=(需要儲存的元素個數 / 負載因子) + 1。注意負載因子(即 loaderfactor)預設為0.75,如果暫時無法確定初始值大小,請設定為16(即預設值)。
反例:HashMap需要放置1024個元素,由於沒有設定容量初始大小,隨著元素不斷增加,容量 7 次被迫擴大,resize需要重建hash表,嚴重影響效能。
2 遍歷Map的方式
【推薦】使用 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-V 值組合集合。
控制語句
1 控制層數不要太高
表達異常的分支時,少用 if-else 方式,這種方式可以改寫
if (condition) {
...
return obj;
}
如果非得使用 if()...else if()...else...方式表達邏輯,避免後續維護護困難,請勿超過 3 層。
超過 3 層的 if-else 的邏輯判斷程式碼可以 其中衛語句示例如下:
public void today() {
if (isBusy()) {
System.out.println(“change time.”);
return;
}
if (isFree()) {
System.out.println(“go to travel.”);
return;
}
System.out.println(“stay at home to learn Alibaba Java Coding Guidelines.”);
return;
}
2 不要在條件中使用複雜的表示式
【推薦】除常用方法(如 getXxx/isXxx)等外,不要在條件判斷中執行其它複雜的語句,將 複雜邏輯判斷的結果賦值給一個有意義的布林變數名,以提高可讀性。 說明:很多 if 語句內的邏輯相當複雜,閱讀者需要分析條件表示式的最終結果,才能明確什麼 樣的條件執行什麼樣的語句,那麼,如果閱讀者分析邏輯表示式錯誤呢? 正例:
// 虛擬碼如下
final boolean existed = (file.open(fileName,"w") != null) && (...) || (...);
if (existed) {
...
}
反例:
if ((file.open(fileName,"w") != null) && (...) || (...)) {
...
}
3 if語句缺少大括號
【強制】在 if/else/for/while/do 語句中必須使用大括號。即使只有一行程式碼,避 單行的編碼方式:if (condition) statements;
註釋規約
1 使用行尾註釋
【強制】方法內部單行註釋,在被註釋語句上方另起一行,使用//註釋。方法內部多行註釋 使用/* */註釋,注意與程式碼對齊。
2 缺少@author資訊
【強制】所有的類都必須新增建立者和建立日期。
@author
@date
3 使用Javadoc註釋
【強制】類、類屬性、類方法的註釋必須使用 Javadoc 規範,使用/*內容/格式,不得使用 // xxx 方式。
說明:在 IDE 編輯視窗中,Javadoc 方式會提示相關注釋,生成 Javadoc 可以正確輸出相應注 釋;在 IDE 中,工程呼叫方法時,不進入方法即可懸浮提示方法、引數、返回值的意義,提高 閱讀效率。
8 及時清理不再使用的程式碼段或配置資訊
【推薦】及時清理不再使用的程式碼段或配
說明:對於垃圾程式碼或過時配置,堅決清理乾淨,避免程式過度臃腫,程式碼冗餘。
正例:對於暫時被註釋掉,後續可能恢復使用的程式碼片斷,在註釋程式碼上方,統一規定使用三 個斜槓(///)來說明註釋掉程式碼的理由。
4 方法的引數缺少javadoc註釋
【強制】所有的抽象方法(包括介面中的方法)必須要用 Javadoc 註釋、除了返回值、引數、 異常說明外,還必須指出該方法做什麼事情,實現什麼功能。
說明:對子類的實現要求,或者呼叫注意事項,請一併說明。
5 列舉欄位缺少註釋
【強制】所有的列舉型別欄位必須要有註釋,說明每個資料項的用途。
其它
1 迴圈體內字串的拼接採用StringBuilder的方式
【推薦】迴圈體內,字串的連線方式,使用 StringBuilder 的 append 方法進行擴充套件。 說明:下例中,反編譯出的位元組碼檔案顯示每次迴圈都會 new 出一個 StringBuilder 物件, 然後進行 append 操作,最後通過 toString 方法返回 String 物件,造成記憶體資源浪費。
反例:
String str = "start";
for (int i = 0; i < 100; i++) {
str = str + "hello";
}
2 顯示建立執行緒池
【強制】執行緒資源必須通過執行緒池提供,不允許在應用中自行顯式建立執行緒。
說明:使用執行緒池的好處是減少在建立和銷燬執行緒上所消耗的時間以及系統資源的開銷,解決 資源不足的問題。如果不使用執行緒池,有可能造成系統建立大量同類執行緒而導致消耗完記憶體或 者“過度切換”的問題。
3 引數列表長度過長
【強制】相同引數型別,相同業務含義,才可以使用 Java 的可變引數,避免使用 Object。 說明:可變引數必須放置在引數列表的最後。(提倡同學們儘量不用可變引數程式設計) 正例:public List listUsers(String type,Long... ids) {...}
4 多個引數進行換行
方法呼叫中的多個引數需要換行時,在逗號後進行。
5 重定向問題
【參考】伺服器內部重定向使用 forward;外部重定向地址使用 URL 拼裝工具類來生成,否則 會帶來 URL 維護不一致的問題和潛在的安全風險。
最後
以上為寫程式碼時最常犯的問題,以及在程式碼規範上提出的處理措施。