前言:
編寫具有規範、易懂的代碼,注意代碼的每一個細節,可以避免稀奇古怪的問題,在一定程度上也能小幅度的提升程式的運作效率。
以下每個例子都是來源于日常的學習與工作中,随着工作年限的提升,我将會不定時地更新此文。
一、簡化if、else
減少if、else判斷的分支。當條件簡單時,可以直接return一個表達式。
優化前: 被攆指數:50%
//判斷該數是否是正數
public boolean isPositive(int num) {
if (num > 0) {
return true;
} else {
return false;
}
}
優化後:
//判斷該數是否是正數
public boolean isPositive(int num) {
return num > 0;
}
4行代碼直接簡化到了1行,并且更加容易閱讀。
當然,優化if、else語句還有很多優化方式,具體可以參考我的這一篇文章優化if else的幾種方式
二、初始化集合時,最好指定容量
如果能在一開始确定往集合存入的元素個數,那麼最好先執行集合的容量,可以有效避免擴容以及記憶體浪費。
優化前: 被攆指數:20%
Map<String, Object> response = new HashMap<>();
response.put("code", 0);
response.put("success", true);
response.put("msg", "action success");
優化後:
Map<String, Object> response = new HashMap<>(3);
response.put("code", 0);
response.put("success", true);
response.put("msg", "action success");
其實我建議這裡指定容量為4可能好點,即不觸發擴容的最小的2的倍數。
當然,這裡設定為3也行,HashMap會自動找到第一個大于此數字的2的倍數,即4,并将4設定為table的初始長度。
接下來會出一篇,講解怎麼去更好地設定HashMap初始容量的文章。
相關的一個話題:為什麼HashMap的容量總是2的倍數?有興趣的同學可以移步到我的另外一篇文章為什麼長度總是2的整數次方
三、避免不必要的手動裝箱或拆箱
當将一個基本資料類型指派給一個對應的包裝類時,會自動進行裝箱。當一個包裝類型與基本資料類型比較時,也會觸發包裝類型的自動拆箱。這些都不需要我們手動的去進行。
優化前: 被攆指數:80%
@Data
private static class User {
private Integer age;
}
public static void main(String[] args) {
User user = new User();
user.setAge(new Integer(12));
//....
if (user.getAge().intValue()<18){
System.out.println("未滿18歲不得...");
}
}
優化後:
@Data
private static class User {
private Integer age;
}
public static void main(String[] args) {
User user = new User();
user.setAge(12);
//....
if (user.getAge() < 18) {
System.out.println("未滿18歲不得...");
}
}
對裝箱和拆箱不熟悉的同學,可以參考我的另外一篇文章談談拆箱與裝箱
四、在代碼中增加事務處理
在多次對資料庫進行操作時,需要增加事務以供出現異常時進行復原。否則輕則資料備援,重則資料錯亂。
優化前: 被攆指數:100%
public void deleteStudent(int studentId){
//删除學生
studentMapper.delete(studentId);
//其他業務操作,可能會出現異常
//删除學生成績
scoreMapper.delete(studentId);
}
優化後:
@Transactional(propagation = Propagation.REQUIRED)
public void deleteStudent(int studentId) {
//删除學生
studentMapper.delete(studentId);
//其他操作,可能會出現異常
//删除學生成績
scoreMapper.delete(studentId);
}
事務的傳播行為還有很多種,有興趣的可以參考我的另外一篇文章Spring事務的傳播行為
當然,如果涉及分布式事務時,情況會更加複雜,這裡不作讨論。
五、複制大數組時,使用System.arraycopy
如果我們在業務中,需要複制一個特别大的數組(以萬為機關),那麼可以考慮使用System.arraycopy
優化前: 被攆指數:40%
//原數組,含有大量元素
int[] src = new int[20000];
//目标數組
int[] des = new int[src.length];
//複制數組
for (int i = 0; i < src.length; i++) {
des[i] = src[i];
}
優化後:
//原數組,含有大量元素
int[] src = new int[20000];
//目标數組
int[] des = new int[src.length];
//複制數組
System.arraycopy(src, 0, des, 0, src.length);
在數組長度機關不同的情況下,複制函數的選擇不是一概而論的,具體可以參考我的這篇文章數組複制效率的比較
六、使用确定值調用equals,或者直接使用Objects.equals
當使用equals進行比較時,為了避免潛在的空指針風險,應該使用确定值發起equals調用。
優化前: 被攆指數:100%
if (user.getName().equals("jack")) {
//其他處理
}
優化後
if ("jack".equals(user.getName())) {
//其他處理
}
//或者使用Objects.equals()
if (Objects.equals(user.getName(), "jack")) {
//其他處理
}
Objects是JDK7時引入的,提供靜态方法操作對象。
這年頭不怕面試官問Object類的方法,就怕問Objects,怕是很多人都不知道有這個類吧。
關于Object類,可以看我的這篇文章Object類的方法簡談
七、線程内使用完ThreadLocal後,記得remove
子線程使用完ThreadLocal時,可以手動調用ThreadLocal的remove方法,将目前ThreadLocal從子線程的ThreadLocalMap中移除。
如果我們隻是一次性的使用該子線程,那麼調用remove方法後,可以在一定程度上避免記憶體洩漏。
如果子線程是從線程池中取出來的,那麼調用remove方法後,可以避免潛在的資料錯亂的問題。
static ThreadLocal<Integer> tl = new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
tl.set(1);
//其他操作
});
t.start();
//等待子線程執行完成
t.join();
//不再使用ThreadLocal
tl = null;
}
static ThreadLocal<Integer> tl = new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
tl.set(1);
//其他操作
tl.remove();
});
t.start();
//等待子線程執行完成
t.join();
//不再使用ThreadLocal
tl = null;
}