Eliminate the "bad smell" of Java code

Posted by Ace_Online on Mon, 16 Sep 2019 12:03:27 +0200

Guide reading

Mr. Wang Yangming of Ming Dynasty said in his Biography that the way of learning was:

Selfish desires are born day by day, like dust on the earth. If you do not sweep them one day, you will have another layer. With practical work, we can see that the Tao is endless. The deeper we explore, the more we will be able to make everything white and incomplete.

The "bad smell" in the code, such as "selfish desire" such as "dust", is increasing every day. If you don't clean up every day, you will get more and more tired. If we work hard to clean up these "bad smells", we can not only improve our coding level, but also make the code "completely white". Here, sorted out some "bad taste" in daily work, and cleaning methods, for your reference.

1. Making Code Performance Higher

1.1. When you need the primary key and value of Map, you should iterate entrySet()

Iterating keySet() is correct when only the primary key of the Map is needed in the loop. However, iterating entrySet() is more efficient when primary keys and values are needed, which is better than iterating keySet() first and then get ting values.

Counter example:

Map<String, String> map = ...;
for (String key : map.keySet()) {
    String value = map.get(key);
    ...
}

Example:

Map<String, String> map = ...;
for (Map.Entry<String, String> entry : map.entrySet()) {
    String key = entry.getKey();
    String value = entry.getValue();
    ...
}

1.2. Collection.isEmpty() should be used to detect null

Using Collection.size() to detect empty logic is no problem, but using Collection.isEmpty() makes the code more readable and achieves better performance. The time complexity of any Collection.isEmpty() implementation is O(1), but the time complexity of some Collection.size() implementations may be O(n).

Counter example:

if (collection.size() == 0) {
    ...
}

Example:

if (collection.isEmpty()) {
    ...
}

Collection Utils. isEmpty (collection) and CollectionUtils.isNotEmpty(collection) can be used if null detection is required.

1.3. Don't pass collection objects to yourself

The way to pass a collection as a parameter to the collection itself is either an error or meaningless code.

In addition, because some methods require parameters to remain unchanged during execution, passing a collection to itself may lead to abnormal behavior.

Counter example:

List<String> list = new ArrayList<>();
list.add("Hello");
list.add("World");
if (list.containsAll(list)) { // It's meaningless. It always returns true.
    ...
}
list.removeAll(list); // Poor performance, direct use of clear()

1.4. Set initialization is as large as possible

java's collection classes are very convenient to use, but as you can see from the source code, the size of the collection is also limited. The time complexity of each expansion is likely to be O(n), so specifying the predictable set size as far as possible can reduce the number of expansion of the set.

Counter example:

int[] arr = new int[]{1, 2, 3};
List<Integer> list = new ArrayList<>();
for (int i : arr) {
    list.add(i);
}

Example:

int[] arr = new int[]{1, 2, 3};
List<Integer> list = new ArrayList<>(arr.length);
for (int i : arr) {
    list.add(i);
}

String Builder for String Mosaic

Generally, string splicing is optimized at compile time in java, but in loops, string splicing can not be optimized at compile time in java, so String Builder is needed to replace it.

Counter example:

String s = "";
for (int i = 0; i < 10; i++) {
    s += i;
}

Example:

String a = "a";
String b = "b";
String c = "c";
String s = a + b + c; // No problem, the java compiler will be optimized
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10; i++) {
    sb.append(i);  // In a loop, the java compiler cannot be optimized, so use StringBuilder manually
}

1.6. Random Access to List

Everyone knows the difference between arrays and linked lists: arrays are more efficient at random access. When a method is called to get a List, what if you want to access the data randomly without knowing whether the internal implementation of the array is a linked list or an array? You can determine whether it implements the Random Access interface.

Example:

// Call someone else's service to get a list
List<Integer> list = otherService.getList();
if (list instanceof RandomAccess) {
    // Internal array implementation, random access
    System.out.println(list.get(list.size() - 1));
} else {
    // Internal may be linked list implementation, random access inefficient
}

1.7. Use Set for frequent calls to Collection.contains methods

In java collection library, the time complexity of contains method of List is O(n). If contains method is frequently called to find data in code, the list can be converted into HashSet implementation first, and the time complexity of O(n) can be reduced to O(1).

Counter example:

ArrayList<Integer> list = otherService.getList();
for (int i = 0; i <= Integer.MAX_VALUE; i++) {
    // Time complexity O(n)
    list.contains(i);
}

Example:

ArrayList<Integer> list = otherService.getList();
Set<Integer> set = new HashSet(list);
for (int i = 0; i <= Integer.MAX_VALUE; i++) {
    // Time complexity O(1)
    set.contains(i);
}

2. Make the code more elegant

2.1. Add capital L after long integer constants

When using long integer constants, we need to add L, which must be capitalized L, not lowercase L. It is easy to confuse lowercase L with number 1 and cause misunderstanding.

Counter example:

long value = 1l;
long max = Math.max(1L, 5);

Example:

long value = 1L;
long max = Math.max(1L, 5L);

2.2. Do not use magic values

When you write a piece of code, using magic values may seem clear, but they don't seem so clear when debugging. That's why magic values need to be defined as readable constants. However, -1, 0 and 1 are not considered magic values.

Counter example:

for (int i = 0; i < 100; i++){
    ...
}
if (a == 100) {
    ...
}

Example:

private static final int MAX_COUNT = 100;
for (int i = 0; i < MAX_COUNT; i++){
    ...
}
if (count == MAX_COUNT) {
    ...
}

2.3. Do not use collection implementations to assign static member variables

For static member variables of collection type, instead of using collection implementations to assign values, static code blocks should be used to assign values.

Counter example:

private static Map<String, Integer> map = new HashMap<String, Integer>() {
    {
        put("a", 1);
        put("b", 2);
    }
};

private static List<String> list = new ArrayList<String>() {
    {
        add("a");
        add("b");
    }
};

Example:

private static Map<String, Integer> map = new HashMap<>();
static {
    map.put("a", 1);
    map.put("b", 2);
};

private static List<String> list = new ArrayList<>();
static {
    list.add("a");
    list.add("b");
};

2.4. The try-with-resources statement is recommended.

In Java 7, try-with-resources statement is introduced, which can ensure the closure of related resources, is better than the original try-catch-final statement, and makes the program code safer and more concise.

Counter example:

private void handle(String fileName) {
    BufferedReader reader = null;
    try {
        String line;
        reader = new BufferedReader(new FileReader(fileName));
        while ((line = reader.readLine()) != null) {
            ...
        }
    } catch (Exception e) {
        ...
    } finally {
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException e) {
                ...
            }
        }
    }
}

Example:

private void handle(String fileName) {
    try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
        String line;
        while ((line = reader.readLine()) != null) {
            ...
        }
    } catch (Exception e) {
        ...
    }
}

2.5. Delete unused private methods and fields

Delete unused private methods and fields to make the code simpler and easier to maintain. If it needs to be reused, it can be retrieved from the history submission.

Counter example:

public class DoubleDemo1 {
    private int unusedField = 100;
    private void unusedMethod() {
        ...
    }
    public int sum(int a, int b) {
        return a + b;
    }
}

Example:

public class DoubleDemo1 {
    public int sum(int a, int b) {
        return a + b;
    }
}

2.6. Delete unused local variables

Delete unused local variables to make the code simpler and easier to maintain.

Counter example:

public int sum(int a, int b) {
    int c = 100;
    return a + b;
}

Example:

public int sum(int a, int b) {
    return a + b;
}

2.7. Delete unused method parameters

Unused method parameters are misleading. Deleting unused method parameters makes the code simpler and easier to maintain. However, since the override method is based on the method definition of the parent class or interface, it cannot be deleted even if there are unused method parameters.

Counter example:

public int sum(int a, int b, int c) {
    return a + b;
}

Example:

public int sum(int a, int b) {
    return a + b;
}

2.8. Delete redundant parentheses of expressions

Redundant parentheses in corresponding expressions are thought to be helpful to code reading, while others consider them totally unnecessary. For a person familiar with Java grammar, extra parentheses in expressions can make the code more cumbersome.

Counter example:

return (x);
return (x + 2);
int x = (y * 3) + 1;
int m = (n * 4 + 2);

Example:

return x;
return x + 2;
int x = y * 3 + 1;
int m = n * 4 + 2;

2.9. Tool classes should shield constructors

Tool classes are a collection of static fields and functions that should not be instantiated. However, java adds an implicit public constructor for each class that does not explicitly define a constructor. Therefore, in order to avoid the misuse of java "whitewash", the private constructor should be explicitly defined to shield the implicit public constructor.

Counter example:

public class MathUtils {
    public static final double PI = 3.1415926D;
    public static int sum(int a, int b) {
        return a + b;
    }
}

Example:

public class MathUtils {
    public static final double PI = 3.1415926D;
    private MathUtils() {}
    public static int sum(int a, int b) {
        return a + b;
    }
}

2.10. Delete redundant exception capture and throw

After catching an exception with the catch statement and doing nothing, the exception is thrown again. This is the same effect as not catching an exception. You can delete this code or add other processing.

Counter example:

private static String readFile(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;
    }
}

Example:

private static String readFile(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();
    }
}

2.11. Public static constants should be accessed through classes

Although accessing public static constants through instances of classes is allowed, it is easy to mistake it for a public static constant for each instance of a class. Therefore, public static constants should be accessed directly through classes.

Counter example:

public class User {
    public static final String CONST_NAME = "name";
    ...
}

User user = new User();
String nameKey = user.CONST_NAME;

Example:

public class User {
    public static final String CONST_NAME = "name";
    ...
}

String nameKey = User.CONST_NAME;

2.12. Don't use NullPointerException to judge null

Null pointer exceptions should be handled by code evasion (for example, detection is not null), rather than by capturing exceptions.

Counter example:

public String getUserName(User user) {
    try {
        return user.getName();
    } catch (NullPointerException e) {
        return null;
    }
}

Example:

public String getUserName(User user) {
    if (Objects.isNull(user)) {
        return null;
    }
    return user.getName();
}

2.13. Use String.valueOf(value) instead of ""+value"

String.valueOf(value) is more efficient than "+value" when converting other objects or types into strings.

Counter example:

int i = 1;
String s = "" + i;

Example:

int i = 1;
String s = String.valueOf(i);

2.14. Add @Deprecated annotation to obsolete code

When a piece of code is out of date and cannot be deleted directly for compatibility, you can tag it with the @Deprecated annotation when you don't want it to be used again in the future. Add @deprecated to the document comment to explain and provide alternatives

Example:

/**
 * Preservation
 *
 * @deprecated This method is inefficient. Replace it with {@link newSave()}
 */
@Deprecated
public void save(){
    // do something
}

3. Keep the code away from bug s

3.1. Prohibit the use of constructor BigDecimal(double)

BigDecimal(double) has the risk of loss of precision, which may lead to business logic anomalies in scenarios of accurate calculation or value comparison.

Counter example:

BigDecimal value = new BigDecimal(0.1D); // 0.100000000000000005551115...

Example:

BigDecimal value = BigDecimal.valueOf(0.1D);; // 0.1

3.2. Return empty arrays and sets instead of null

Returning null requires the caller to force null detection, otherwise null pointer exceptions will be thrown. Returning an empty array or set effectively avoids the null pointer exception thrown by the caller because null is not detected, and can also delete the statement that the caller detects null to make the code more concise.

Counter example:

public static Result[] getResults() {
    return null;
}

public static List<Result> getResultList() {
    return null;
}

public static Map<String, Result> getResultMap() {
    return null;
}

public static void main(String[] args) {
    Result[] results = getResults();
    if (results != null) {
        for (Result result : results) {
            ...
        }
    }

    List<Result> resultList = getResultList();
    if (resultList != null) {
        for (Result result : resultList) {
            ...
        }
    }

    Map<String, Result> resultMap = getResultMap();
    if (resultMap != null) {
        for (Map.Entry<String, Result> resultEntry : resultMap) {
            ...
        }
    }
}

Example:

public static Result[] getResults() {
    return new Result[0];
}

public static List<Result> getResultList() {
    return Collections.emptyList();
}

public static Map<String, Result> getResultMap() {
    return Collections.emptyMap();
}

public static void main(String[] args) {
    Result[] results = getResults();
    for (Result result : results) {
        ...
    }

    List<Result> resultList = getResultList();
    for (Result result : resultList) {
        ...
    }

    Map<String, Result> resultMap = getResultMap();
    for (Map.Entry<String, Result> resultEntry : resultMap) {
        ...
    }
}

3.3. Use constants or deterministic values first to call equals methods

The equals method of an object is easy to throw null pointer exceptions, so the equals method should be invoked using constants or identifying objects with values. Of course, using java.util.Objects.equals() is a best practice.

Counter example:

public void isFinished(OrderStatus status) {
    return status.equals(OrderStatus.FINISHED); // Potential null pointer exception
}

Example:

public void isFinished(OrderStatus status) {
    return OrderStatus.FINISHED.equals(status);
}

public void isFinished(OrderStatus status) {
    return Objects.equals(status, OrderStatus.FINISHED);
}

3.4. Enumerated property fields must be private and immutable

Enumerations are often used as constants, and if there are common property fields or set field methods in enumerations, the properties of these enumeration constants can easily be modified. Ideally, the attribute fields in the enumeration are private and assigned to private constructors. There is no corresponding Setter method, and it is better to add final modifiers.

Counter example:

public enum UserStatus {
    DISABLED(0, "Prohibit"),
    ENABLED(1, "Enable");

    public int value;
    private String description;

    private UserStatus(int value, String description) {
        this.value = value;
        this.description = description;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

Example:

public enum UserStatus {
    DISABLED(0, "Prohibit"),
    ENABLED(1, "Enable");

    private final int value;
    private final String description;

    private UserStatus(int value, String description) {
        this.value = value;
        this.description = description;
    }

    public int getValue() {
        return value;
    }

    public String getDescription() {
        return description;
    }
}

3.5. Be careful of String.split(String regex)

String split method, the incoming separating string is a regular expression! Some keywords (e.g. []()| etc.) need to be escaped

Counter example:

"a.ab.abc".split("."); // The result is [].
"a|ab|abc".split("|"); // The results are ["a", "|", "a", "b", "|", "a", "b", "c"]

Example:

"a.ab.abc".split("\\."); // The results are ["a", "ab", "abc"]
"a|ab|abc".split("\\|"); // The results are ["a", "ab", "abc"]

summary

This article, can be said to be engaged in Java development experience summary, share out for your reference. Hope to help you avoid trampling holes, so that the code is more efficient and elegant.


Original link
This article is the original content of Yunqi Community, which can not be reproduced without permission.

Topics: Java Attribute