反正舉例教你規範寫程式碼

安全劍客發表於2020-10-19
本文介紹了十六項如何規範的寫出程式碼,我們一起來看一下吧。

反正舉例教你規範寫程式碼反正舉例教你規範寫程式碼

一、MyBatis 不要為了多個查詢條件而寫 1 = 1

當遇到多個查詢條件,使用where 1=1 可以很方便的解決我們的問題,但是這樣很可能會造成非常大的效能損失,因為新增了 “where 1=1 ”的過濾條件之後,資料庫系統就無法使用索引等查詢最佳化策略,資料庫系統將會被迫對每行資料進行掃描(即全表掃描) 以比較此行是否滿足過濾條件,當表中的資料量較大時查詢速度會非常慢;此外,還會存在SQL 注入的風險。

反例:
反正舉例教你規範寫程式碼反正舉例教你規範寫程式碼
正例:
反正舉例教你規範寫程式碼反正舉例教你規範寫程式碼
UPDATE 操作也一樣,可以用標記代替 1=1。

二、迭代entrySet() 獲取Map 的key 和value

當迴圈中只需要獲取Map 的主鍵key時,迭代keySet() 是正確的;但是,當需要主鍵key 和取值value 時,迭代entrySet() 才是更高效的做法,其比先迭代keySet() 後再去透過get 取值效能更佳。

反例:

    //Map 獲取value 反例:  
    HashMapmap = new HashMap<>();  
    for (String key : map.keySet()){  
        String value = map.get(key);  
    }

正例:

    //Map 獲取key & value 正例:  
    HashMapmap = new HashMap<>();  
    for (Map.Entryentry : map.entrySet()){  
     String key = entry.getKey();  
     String value = entry.getValue();  
    }
三、使用Collection.isEmpty() 檢測空

使用Collection.size() 來檢測是否為空在邏輯上沒有問題,但是使用Collection.isEmpty() 使得程式碼更易讀,並且可以獲得更好的效能;除此之外,任何Collection.isEmpty() 實現的時間複雜度都是O(1) ,不需要多次迴圈遍歷,但是某些透過Collection.size() 方法實現的時間複雜度可能是O(n)

反例:

    LinkedList collection = new LinkedList<>();  
    if (collection.size() == 0){  
     System.out.println("collection is empty.");  
    }

正例:

    LinkedList collection = new LinkedList<>();  
    if (collection.isEmpty()){  
        System.out.println("collection is empty.");  
    }  
    //檢測是否為null 可以使用CollectionUtils.isEmpty()  
    if (CollectionUtils.isEmpty(collection)){ 
         System.out.println("collection is null.");  
    }
四、初始化集合時儘量指定其大小

儘量在初始化時指定集合的大小,能有效減少集合的擴容次數,因為集合每次擴容的時間複雜度很可能時O(n),耗費時間和效能。

反例:

    //初始化list,往list 中新增元素反例: 
     
    int[] arr = new int[]{1,2,3,4};  
    Listlist = new ArrayList<>();  
    for (int i : arr){  
     list.add(i);  
    }

正例:

    //初始化list,往list 中新增元素正例:  
    int[] arr = new int[]{1,2,3,4};  
    //指定集合list 的容量大小  
    Listlist = new ArrayList<>(arr.length);  
    for (int i : arr){  
        list.add(i);  
    }
五、使用StringBuilder 拼接字串

一般的字串拼接在編譯期Java 會對其進行最佳化,但是在迴圈中字串的拼接Java 編譯期無法執行最佳化,所以需要使用StringBuilder 進行替換。

反例:

    //在迴圈中拼接字串反例  
    String str = "";  
    for (int i = 0; i < 10; i++){  
        //在迴圈中字串拼接Java 不會對其進行最佳化  
        str += i;  
    }

正例:

    //在迴圈中拼接字串正例  
    String str1 = "Love";  
    String str2 = "Courage";  
    String strConcat = str1 + str2;  //Java 編譯器會對該普通模式的字串拼接進行最佳化  
    StringBuilder sb = new StringBuilder();  
    for (int i = 0; i < 10; i++){  
       //在迴圈中,Java 編譯器無法進行最佳化,所以要手動使用StringBuilder  
        sb.append(i);  
    }
六、若需頻繁呼叫Collection.contains 方法則使用Set

在Java 集合類庫中,List的contains 方法普遍時間複雜度為O(n),若程式碼中需要頻繁呼叫contains 方法查詢資料則先將集合list 轉換成HashSet 實現,將O(n) 的時間複雜度將為O(1)。

反例:

    //頻繁呼叫Collection.contains() 反例  
    List list = new ArrayList<>();  
    for (int i = 0; i <= Integer.MAX_VALUE; i++){  
        //時間複雜度為O(n)  
        if (list.contains(i))  
        System.out.println("list contains "+ i);  
    }

正例:

    //頻繁呼叫Collection.contains() 正例  
    List list = new ArrayList<>();  
    Set set = new HashSet<>();  
    for (int i = 0; i <= Integer.MAX_VALUE; i++){  
        //時間複雜度為O(1)  
        if (set.contains(i)){  
            System.out.println("list contains "+ i);  
        }  
    }
七、使用靜態程式碼塊實現賦值靜態成員變數

對於集合型別的靜態成員變數,應該使用靜態程式碼塊賦值,而不是使用集合實現來賦值。

反例:

    //賦值靜態成員變數反例 
     private static Mapmap = new HashMap(){  
        {  
            map.put("Leo",1);  
            map.put("Family-loving",2);  
            map.put("Cold on the out side passionate on the inside",3);  
        }  
    }; 
     private static Listlist = new ArrayList<>(){  
        {  
            list.add("Sagittarius");  
            list.add("Charming");  
            list.add("Perfectionist");  
        }  
    };

正例:

    //賦值靜態成員變數正例  
    private static Mapmap = new HashMap();  
    static {  
        map.put("Leo",1);  
        map.put("Family-loving",2);  
        map.put("Cold on the out side passionate on the inside",3);  
    }  
    private static Listlist = new ArrayList<>(); 
     static {  
        list.add("Sagittarius");  
        list.add("Charming");  
        list.add("Perfectionist");  
    }
八、刪除未使用的區域性變數、方法引數、私有方法、欄位和多餘的括號。
九、工具類中遮蔽建構函式

工具類是一堆靜態欄位和函式的集合,其不應該被例項化;但是,Java 為每個沒有明確定義建構函式的類新增了一個隱式公有建構函式,為了避免不必要的例項化,應該顯式定義私有建構函式來遮蔽這個隱式公有建構函式。

反例:

    public class PasswordUtils {  
    //工具類建構函式反例  
    private static final Logger LOG = LoggerFactory.getLogger(PasswordUtils.class);  
    public static final String DEFAULT_CRYPT_ALGO = "PBEWithMD5AndDES"; 
    public static String encryptPassword(String aPassword) throws IOException {  
        return new PasswordUtils(aPassword).encrypt();  
    }

正例:

    public class PasswordUtils {  
    //工具類建構函式正例  
    private static final Logger LOG = LoggerFactory.getLogger(PasswordUtils.class);  
    //定義私有建構函式來遮蔽這個隱式公有建構函式  
    private PasswordUtils(){} 
    public static final String DEFAULT_CRYPT_ALGO = "PBEWithMD5AndDES";  
    public static String encryptPassword(String aPassword) throws IOException {  
        return new PasswordUtils(aPassword).encrypt();  
    }
十、刪除多餘的異常捕獲並跑出

用catch 語句捕獲異常後,若什麼也不進行處理,就只是讓異常重新丟擲,這跟不捕獲異常的效果一樣,可以刪除這塊程式碼或新增別的處理。

反例:

    //多餘異常反例  
    private static String fileReader(String fileName)throws IOException{  
        try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {  
            String line;  
            StringBuilder builder = new StringBuilder();  
            while ((line = reader.readLine()) != null) {  
                builder.append(line);  
            }  
            return builder.toString();  
        } catch (Exception e) {  
            //僅僅是重複拋異常 未作任何處理  
            throw e;  
        }  
    }

正例:

    //多餘異常正例  
    private static String fileReader(String fileName)throws IOException{  
        try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {  
            String line;  
            StringBuilder builder = new StringBuilder();  
            while ((line = reader.readLine()) != null) {  
                builder.append(line);  
            }  
            return builder.toString();  
            //刪除多餘的拋異常,或增加其他處理:  
            /*catch (Exception e) {  
                return "fileReader exception";  
            }*/  
        }  
    }
十一、字串轉化使用String.valueOf(value) 代替 " " + value

把其它物件或型別轉化為字串時,使用String.valueOf(value) 比 ""+value 的效率更高。

反例:

    //把其它物件或型別轉化為字串反例:  
    int num = 520;  
    // "" + value  
    String strLove = "" + num;

正例:

    //把其它物件或型別轉化為字串正例:  
    int num = 520;  
    // String.valueOf() 效率更高  
    String strLove = String.valueOf(num);
十二、避免使用BigDecimal(double)

BigDecimal(double) 存在精度損失風險,在精確計算或值比較的場景中可能會導致業務邏輯異常。

反例:

    // BigDecimal 反例      
    BigDecimal bigDecimal = new BigDecimal(0.11D);

正例:

    // BigDecimal 正例  
    BigDecimal bigDecimalbigDecimal1 = bigDecimal.valueOf(0.11D);
十三、返回空陣列和集合而非 null

若程式執行返回null,需要呼叫方強制檢測null,否則就會丟擲空指標異常;返回空陣列或空集合,有效地避免了呼叫方因為未檢測null 而丟擲空指標異常的情況,還可以刪除呼叫方檢測null 的語句使程式碼更簡潔。

反例:

    //返回null 反例  
    public static Result[] getResults() {  
        return null; 
     }  
    public static ListgetResultList() {  
        return null;  
    }  
    public static MapgetResultMap() {  
        return null;  
    }

正例:

    //返回空陣列和空集正例  
    public static Result[] getResults() {  
        return new Result[0];  
    }  
    public static ListgetResultList() {  
        return Collections.emptyList();  
    }  
    public static MapgetResultMap() {  
        return Collections.emptyMap();  
    }
十四、優先使用常量或確定值呼叫equals 方法

物件的equals 方法容易拋空指標異常,應使用常量或確定有值的物件來呼叫equals 方法。

反例:

    //呼叫 equals 方法反例  
    private static boolean fileReader(String fileName)throws IOException{  
     // 可能拋空指標異常 
      return fileName.equals("Charming");  
    }

正例:

    //呼叫 equals 方法正例  
    private static boolean fileReader(String fileName)throws IOException{  
        // 使用常量或確定有值的物件來呼叫 equals 方法  
        return "Charming".equals(fileName);   
        //或使用:java.util.Objects.equals() 方法  
       return Objects.equals("Charming",fileName);  
    }
十五、列舉的屬性欄位必須是私有且不可變

列舉通常被當做常量使用,如果列舉中存在公共屬性欄位或設定欄位方法,那麼這些列舉常量的屬性很容易被修改;理想情況下,列舉中的屬性欄位是私有的,並在私有建構函式中賦值,沒有對應的Setter 方法,最好加上final 修飾符。

反例:

    public enum SwitchStatus {  
        // 列舉的屬性欄位反例  
        DISABLED(0, "禁用"),  
        ENABLED(1, "啟用");  
        public int value;  
        private String description;  
        private SwitchStatus(int value, String description) {  
            this.value = value;  
            this.description = description;  
        }  
        public String getDescription() {  
            return description;  
        }  
        public void setDescription(String description) {  
            this.description = description;  
        }  
    }

正例:

    public enum SwitchStatus {  
        // 列舉的屬性欄位正例  
        DISABLED(0, "禁用"),  
        ENABLED(1, "啟用");  
        // final 修飾  
        private final int value;  
        private final String description;  
        private SwitchStatus(int value, String description) {  
            this.value = value;  
            this.description = description;  
        }  
        // 沒有Setter 方法  
        public int getValue() {  
            return value;  
        }  
        public String getDescription() {  
            return description;  
        }  
    }
十六、tring.split(String regex)部分關鍵字需要轉譯

使用字串String 的plit 方法時,傳入的分隔字串是正規表示式,則部分關鍵字(比如 .[]()| 等)需要轉義。

反例:

    // String.split(String regex) 反例  
    String[] split = "a.ab.abc".split(".");  
    System.out.println(Arrays.toString(split));   // 結果為[]  
    String[] split1 = "a|ab|abc".split("|");  
    System.out.println(Arrays.toString(split1));  // 結果為["a", "|", "a", "b", "|", "a", "b", "c"]

正例:

    // String.split(String regex) 正例  
    // . 需要轉譯  
    String[] split2 = "a.ab.abc".split("\\.");  
    System.out.println(Arrays.toString(split2));  // 結果為["a", "ab", "abc"]  
    // | 需要轉譯  
    String[] split3 = "a|ab|abc".split("\\|");  
    System.out.println(Arrays.toString(split3));  // 結果為["a", "ab", "abc"]

原文地址:

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31559985/viewspace-2727704/,如需轉載,請註明出處,否則將追究法律責任。

相關文章