目前大部分團隊是使用的阿里巴巴Java開發規範,不過在日常開發中難免遇到覆蓋不到的場景,本文在阿里巴巴Java開發規範基礎上,補充一些常用的規範,用於提升程式碼質量及增強程式碼可讀性。
String型別轉數位:使用apache common-lang3包中的工具類NumberUtils,優勢:可設定預設值,轉換出錯時返回預設值
NumberUtils.toInt("1");
拆箱:包裝類轉化為基本型別的時候,需要判定null,比如:
Integer numObject = param.get(0);
int num = numObject != null ? numObject : 0;
使用MapStruct工具,轉換類字尾Convertor,所有轉換操作都在轉換類中操作,禁止在業務程式碼中編寫大量set程式碼。
使用列舉判等,而非列舉對應的數位。因為列舉更直觀,方便檢視程式碼及偵錯,數位容易出錯。
各種物件的判空:
//物件判空&非空
Objects.isNull()
Objects.nonNull()
//String判空&非空
StringUtils.isEmpty() //可匹配null和空字串
StringUtils.isNotEmpty()
StringUtils.isBlank() //可匹配null、空字串、多個空白字元
StringUtils.isNotBlank()
//集合判空&非空
CollectionUtils.isEmpty()
CollectionUtils.isNotEmpty()
//Map判空&非空
MapUtils.isEmpty()
MapUtils.isNotEmpty()
使用Guava裡的Preconditions工具類,比如:
//如果是空則拋異常
Preconditions.checkNotNull()
//通用判斷
Preconditions.checkArgument()
推薦:
//如果值不存在則計算
map.computeIfAbsent("key",k-> execValue(k));
//預設值
map.getOrDefault("key", DEFAULT_VALUE)
反例:
//如果值不存在則計算
String v = map.get("key");
if(v == null){
v = execValue("key");
map.put("key", v);
}
//預設值
map.containsKey("key") ? map.get("key") : DEFAULT_VALUE
構造方法或Builder模式,超過3個引數物件建立使用Builder模式
//Java11+:
List.of(1, 2, 3)
Set.of(1, 2, 3)
Map.of("a", 1)
//Java8中不可變集合(需引入Guava)
ImmutableList.of(1,2,3)
ImmutableSet.of(1,2,3)
ImmutableMap.of("key","value")
//多值情況
ImmutableMap.builder()
.put("key", "value")
.put("key2", "value2")
.build()
//Java8中可變集合(需引入Guava)
Lists.newArrayList(1, 2, 3)
Sets.newHashSet(1, 2, 3)
Maps.newHashMap("key", "value")
反例:
new ArrayList<>(){{
add(1);
add(2);
}};
集合裡的值如果是基礎型別必須加上註釋,說明集合裡存的是什麼,比如:
//返回值: Map(key: 姓名, value: List(商品))
Map<String, List<String>> res;
超過2層集合物件封裝必須封裝成自定義類:
//推薦
Map<String, List<Node>> res;
@Value
public static class Node {
/**
* 備註說明欄位
*/
String name;
/**
* 備註說明欄位2
*/
List<Integer> subjectIds;
}
//反例
Map<String, List<Pair<String, List<Integer>>>> res;
關於異常及錯誤碼的思考,請參考筆者的另一篇文章:錯誤碼設計思考
異常除了拋異常還有一種場景,即:上層發起多個必要呼叫,某些可能失敗,需要上層自行決定處理策略,推薦使用vavr中的Either類,Either使用建議:通常我們使用左值表示異常,而右值表示正常呼叫後的返回結果,即: Either<Throwable, Data>
根據紀錄檔等級一般分為4個紀錄檔檔案即可:debug.log、info.log、warn.log、error.log;
如有特殊需求可根據場景單獨建檔案,比如請求紀錄檔:request.log、gc紀錄檔:gc.log等。
追蹤欄位包括:traceId、userId等,推薦使用MDC,常用的紀錄檔框架:Log4j、Logback都支援。
本地紀錄檔根據磁碟大小,必須設定紀錄檔儲存天數,否則有硬碟滿風險;
分散式環境為了方便查詢,需要將紀錄檔採集到ES中查詢;
重要紀錄檔:比如審計紀錄檔、B端操作紀錄檔需要持久儲存,一般是儲存到Hive中;
推薦:使用Gson或Jackson;
不推薦:Fastjson。Fastjson爆出的漏洞多。
推薦:MapStruct,根據註解編譯成Java程式碼,沒有反射,速度快;行為可預測,可檢視編譯後的Java程式碼檢視轉換邏輯;
不推薦:BeanUtils、Dozer等。需要反射,行為不可預測,需要測試;
不推薦:超過3個欄位手動轉換;
推薦:Lombok,減少程式碼行數,提升開發效率,自動生成Java程式碼,沒有效能損耗;
不推薦:手動生成大量set、get方法;
推薦:hibernate Validation、spring-boot-starter-validation,可通過註解自動實現引數攔截;
不推薦:每個入口(比如Controller)都copy大量重複的校驗邏輯;
推薦:Spring Cache,通過註解控制快取邏輯,適合常用的加快取場景。
正向語意的好處在於使程式碼容易理解。 比如:if(judge()){....},很容易理解,即:判定成功則執行程式碼塊。
相反,如果是負向語意,思維還要轉換一下,一般用於方法前置的引數校驗。
正向語意的應用場景有:
反例:
if (!judge()) {
doSomething2()
} else {
doSomething()
}
外部傳過來資料都需要校驗,一般分為兩類:
如果是資料流入,一定要首先校驗資料合法性再往下執行,推薦hibernate Validation這類工具,可以很方便的做資料校驗
資料是資料依賴,一定要考慮各種網路、限流、背壓等場景,做好熔斷、降級保障。推薦建立防腐層,將第三方的限界上下文語意轉換為當前上下文語意,避免理解上的歧義;
對於強依賴,沒有返回值不行(比如查詢資料庫):直接拋異常;
需要反饋給上層處理:
(1)可能返回null的場景:使用Optional;
(2)上層需要感知資訊異常資訊:使用vavr中的Either;
可降級:
(1)返回值是預設值:集合類返回,數位返回0或-1,字串返回空字串,其他場景自定義
集合預設值:
Collections.emptyList() //空List
Collections.emptySet() //空Set
Collections.emptyMap() //空Map
本文總結了Java開發常用的高階規範,暫時想到這麼多,對文章中觀點感興趣,歡迎留言或加微信交流。
作者部落格連結:Java研發規範(進階版)
作者簡介:木小豐,美團Java技術專家,專注分享軟體研發實踐、架構思考。歡迎關注公共號:Java研發
更多精彩文章: