這樣寫程式碼,直接被主管攆出去

2020-10-01 11:00:26

前言:

編寫具有規範、易懂的程式碼,注意程式碼的每一個細節,可以避免稀奇古怪的問題,在一定程度上也能小幅度的提升程式的執行效率。

以下每個例子都是來源於日常的學習與工作中,隨著工作年限的提升,我將會不定時地更新此文。


一、簡化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方法後,可以避免潛在的資料錯亂的問題。

優化前:                                                                                 被攆指數:50%

    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;
    }

ThreadLocal使用不當,確實會引起可能的記憶體漏失。我的這篇文章ThreadLocal使用不好,小心造成記憶體洩露!從直觀易懂的參照圖開始,分析了記憶體漏失的原因。


持續更新...