1.1.利用構造方法編程
構造方法,能夠簡化對象的初始化和設置屬性操做。對於屬性字段較少的類,能夠自定義構造方法。json
普通:數組
@Getter
@Setter
@ToString
public class PageDataVO<T> {
private Long totalCount;
private List<T> dataList;
}
PageDataVO<UserVO> pageData = new PageDataVO<>();
pageData.setTotalCount(totalCount);
pageData.setDataList(userList);
return pageData;
精簡:安全
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class PageDataVO<T> {
private Long totalCount;
private List<T> dataList;
}
return new PageDataVO<>(totalCount, userList);
注意:若是屬性字段被替換時,存在構造函數初始化賦值問題。好比把屬性字段title替換爲 nickname ,因爲構造函數的參數個數和類型不變,原有構造函數初始化語句不會報錯,致使把原title值賦值給 nickname 。若是採用 Setter 方法賦值,編譯器會提示錯誤並要求修復。數據結構
利用 Set 的 add 方法的返回值,能夠直接知道該值是否已經存在,能夠避免調用 contains 方法判斷存在。app
普通:ide
如下案例是進行用戶去重轉化操做,須要先調用 contains 方法判斷存在,後調用add方法進行添加。函數
Set<Long> userIdSet = new HashSet<>();
List<UserVO> userVOList = new ArrayList<>();
for (UserDO userDO : userDOList) {
if (!userIdSet.contains(userDO.getId())) {
userIdSet.add(userDO.getId());
userVOList.add(transUser(userDO));
}
}
精簡:
工具
Set<Long> userIdSet = new HashSet<>();
List<UserVO> userVOList = new ArrayList<>();
for (UserDO userDO : userDOList) {
if (userIdSet.add(userDO.getId())) {
userVOList.add(transUser(userDO));
}
}
利用 Map 的 computeIfAbsent 方法,能夠保證獲取到的對象非空,從而避免了沒必要要的空判斷和從新設置值。性能
普通:
Map<Long, List<UserDO>> roleUserMap = new HashMap<>();
for (UserDO userDO : userDOList) {
Long roleId = userDO.getRoleId();
List<UserDO> userList = roleUserMap.get(roleId);
if (Objects.isNull(userList)) {
userList = new ArrayList<>();
roleUserMap.put(roleId, userList);
}
userList.add(userDO);
}
精簡:
Map<Long, List<UserDO>> roleUserMap = new HashMap<>();
for (UserDO userDO : userDOList) {
roleUserMap.computeIfAbsent(userDO.getRoleId(), key -> new ArrayList<>())
.add(userDO);
}
鏈式編程,也叫級聯式編程,調用對象的函數時返回一個this對象指向對象自己,達到鏈式效果,能夠級聯調用。鏈式編程的優勢是:編程性強、可讀性強、代碼簡潔。
普通
StringBuilder builder = new StringBuilder(96);
builder.append("select id, name from ");
builder.append(T_USER);
builder.append(" where id = ");
builder.append(userId);
builder.append(";");
精簡:
StringBuilder builder = new StringBuilder(96);
builder.append("select id, name from ")
.append(T_USER)
.append(" where id = ")
.append(userId)
.append(";");
2.1.避免空值判斷
普通:
if (userList != null && !userList.isEmpty()) {
// TODO: 處理代碼
}
精簡:
if (CollectionUtils.isNotEmpty(userList)) {
// TODO: 處理代碼
}
普通:
double result;
if (value <= MIN_LIMIT) {
result = MIN_LIMIT;
} else {
result = value;
}
精簡:
double result = Math.max(MIN_LIMIT, value);
普通:
public static final List<String> ANIMAL_LIST;
static {
List<String> animalList = new ArrayList<>();
animalList.add("dog");
animalList.add("cat");
animalList.add("tiger");
ANIMAL_LIST = Collections.unmodifiableList(animalList);
}
精簡:
// JDK流派
public static final List<String> ANIMAL_LIST = Arrays.asList("dog", "cat", "tiger");
// Guava流派
public static final List<String> ANIMAL_LIST = ImmutableList.of("dog", "cat", "tiger");
注意:Arrays.asList 返回的 List 並非 ArrayList ,不支持 add 等變動操做。
普通:
UserVO userVO = new UserVO();
userVO.setId(userDO.getId());
userVO.setName(userDO.getName());
...
userVO.setDescription(userDO.getDescription());
userVOList.add(userVO);
精簡:
UserVO userVO = new UserVO();
BeanUtils.copyProperties(userDO, userVO);
userVOList.add(userVO);
2.5.簡化異常斷言
普通:
if (Objects.isNull(userId)) {
throw new IllegalArgumentException("用戶標識不能爲空");
}
精簡:
Assert.notNull(userId, "用戶標識不能爲空");
注意:可能有些插件不認同這種判斷,致使使用該對象時會有空指針警告。
把測試用例數據以 JSON 格式存入文件中,經過 JSON 的 parseObject 和 parseArray 方法解析成對象。雖然執行效率上有所降低,但能夠減小大量的賦值語句,從而精簡了測試代碼。
普通:
@Test
public void testCreateUser() {
UserCreateVO userCreate = new UserCreateVO();
userCreate.setName("Changyi");
userCreate.setTitle("Developer");
userCreate.setCompany("AMAP");
...
Long userId = userService.createUser(OPERATOR, userCreate);
Assert.assertNotNull(userId, "建立用戶失敗");
}
精簡:
@Test
public void testCreateUser() {
String jsonText = ResourceHelper.getResourceAsString(getClass(), "createUser.json");
UserCreateVO userCreate = JSON.parseObject(jsonText, UserCreateVO.class);
Long userId = userService.createUser(OPERATOR, userCreate);
Assert.assertNotNull(userId, "建立用戶失敗");
}
建議:JSON 文件名最好以被測試的方法命名,若是有多個版本能夠用數字後綴表示。
3.1.利用數組簡化
對於固定上下限範圍的 if-else 語句,能夠用數組+循環來簡化。
普通:
public static int getGrade(double score) {
if (score >= 90.0D) {
return 1;
}
if (score >= 80.0D) {
return 2;
}
if (score >= 60.0D) {
return 3;
}
if (score >= 30.0D) {
return 4;
}
return 5;
}
精簡:
private static final double[] SCORE_RANGES = new double[] {90.0D, 80.0D, 60.0D, 30.0D};
public static int getGrade(double score) {
for (int i = 0; i < SCORE_RANGES.length; i++) {
if (score >= SCORE_RANGES[i]) {
return i + 1;
}
}
return SCORE_RANGES.length + 1;
}
思考:上面的案例返回值是遞增的,因此用數組簡化是沒有問題的。可是,若是返回值不是遞增的,可否用數組進行簡化呢?答案是能夠的,請自行思考解決。
3.2.利用 Map 簡化
對於映射關係的 if-else 語句,能夠用Map來簡化。此外,此規則一樣適用於簡化映射關係的 switch 語句。
普通:
public static String getBiologyClass(String name) {
switch (name) {
case "dog" :
return "animal";
case "cat" :
return "animal";
case "lavender" :
return "plant";
...
default :
return null;
}
}
精簡:
private static final Map<String, String> BIOLOGY_CLASS_MAP
= ImmutableMap.<String, String>builder()
.put("dog", "animal")
.put("cat", "animal")
.put("lavender", "plant")
...
.build();
public static String getBiologyClass(String name) {
return BIOLOGY_CLASS_MAP.get(name);
}
已經把方法簡化爲一行代碼,其實都沒有封裝方法的必要了。
Java 不像 Python 和 Go ,方法不支持返回多個對象。若是須要返回多個對象,就必須自定義類,或者利用容器類。常見的容器類有 Apache 的 Pair 類和 Triple 類, Pair 類支持返回 2 個對象, Triple 類支持返回 3 個對象
普通:
@Setter
@Getter
@ToString
@AllArgsConstructor
public static class PointAndDistance {
private Point point;
private Double distance;
}
public static PointAndDistance getNearest(Point point, Point[] points) {
// 計算最近點和距離
...
// 返回最近點和距離
return new PointAndDistance(nearestPoint, nearestDistance);
}
精簡:
public static Pair<Point, Double> getNearest(Point point, Point[] points) {
// 計算最近點和距離
...
// 返回最近點和距離
return ImmutablePair.of(nearestPoint, nearestDistance);
}
ThreadLocal 提供了線程專有對象,能夠在整個線程生命週期中隨時取用,極大地方便了一些邏輯的實現。用 ThreadLocal 保存線程上下文對象,能夠避免沒必要要的參數傳遞。
普通:
因爲 DateFormat 的 format 方法線程非安全(建議使用替代方法),在線程中頻繁初始化 DateFormat 性能過低,若是考慮重用只能用參數傳入 DateFormat 。例子以下:
public static String formatDate(Date date, DateFormat format) {精簡:
return format.format(date);
}
public static List<String> getDateList(Date minDate, Date maxDate, DateFormat format) {
List<String> dateList = new ArrayList<>();
Calendar calendar = Calendar.getInstance();
calendar.setTime(minDate);
String currDate = formatDate(calendar.getTime(), format);
String maxsDate = formatDate(maxDate, format);
while (currDate.compareTo(maxsDate) <= 0) {
dateList.add(currDate);
calendar.add(Calendar.DATE, 1);
currDate = formatDate(calendar.getTime(), format);
}
return dateList;
}
可能你會以爲如下的代碼量反而多了,若是調用工具方法的地方比較多,就能夠省下一大堆 DateFormat 初始化和傳入參數的代碼。
private static final ThreadLocal<DateFormat> LOCAL_DATE_FORMAT = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyyMMdd");
}
};
public static String formatDate(Date date) {
return LOCAL_DATE_FORMAT.get().format(date);
}
public static List<String> getDateList(Date minDate, Date maxDate) {
List<String> dateList = new ArrayList<>();
Calendar calendar = Calendar.getInstance();
calendar.setTime(minDate);
String currDate = formatDate(calendar.getTime());
String maxsDate = formatDate(maxDate);
while (currDate.compareTo(maxsDate) <= 0) {
dateList.add(currDate);
calendar.add(Calendar.DATE, 1);
currDate = formatDate(calendar.getTime());
}
return dateList;
}
注意:ThreadLocal 有必定的內存泄露的風險,儘可能在業務代碼結束前調用 remove 方法進行數據清除。