Цицзи прыгает, но не может сделать и десяти шагов; Улучшайте качество кода и оптимизируйте производительность, самое дорогое — это настойчивость! Если вы усердно работаете над устранением «дурного запаха» кода и настаиваете на его накоплении, вы можете не только повысить свой уровень кодирования, но и сделать код «своим».Ничто не идеально".
В нашей серверной разработке мы часто сталкиваемся с различными «ямами», такими как наиболее знакомое исключение нулевого указателя, аннулирование сеанса и т. д. На самом деле, большая причина в том, что процесс разработки плохо сочетает использование с практикой, если мы можете обратить внимание на некоторые спецификации кода и навыки, мы можем сделать нашу разработку более эффективной! Я поделюсь оптимизациями, которые я сделал во время разработки, по следующим трем аспектам.
- Сделайте код более производительным
- Сделайте код более элегантным
- Не допускайте ошибок в коде
Сделайте код более производительным
1. Когда вам нужен первичный ключ и значение карты, вы должны итерации WhainSet ()
Когда для цикла требуется только первичный ключ Map, итерация keySet() является правильной. Однако, когда вам нужен первичный ключ и значение, итеративный entrySet() является более эффективным подходом, чем первый итерационный keySet(), а затем перейти к получению значения с лучшей производительностью.
Контрпример
Map<String,String> map = ...;
for(String key: map.keySet()) {
String value = map.get(key);
...
}
Положительный пример
Map<String,String> map = ...;
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
...
}
2. Collection.isEmpty() следует использовать для обнаружения пустых
Использование Collection.size() для обнаружения пустоты логически правильно, но использование Collection.isEmpty() делает код более читабельным и приводит к повышению производительности. Временная сложность любой реализации Collection.isEmpty() составляетO(1), но временная сложность некоторых реализаций Collection.size() может бытьO(n).
Контрпример
if (collection.size() == 0) {
...
}
Положительный пример
if (collection.isEmpty()) {
...
}
3. Установите инициализацию как можно больше, чтобы указать размер
Класс-коллекция в Java очень удобен в использовании, но глядя на исходный код, видно, что коллекция также ограничена по размеру. Временная сложность каждого расширения, вероятно, будет O(n), поэтому постарайтесь указать предсказуемый размер набора, чтобы уменьшить количество расширений набора.
Контрпример
int[] arr = new int[]{1, 2, 3};
List<Integer> list = new ArrayList<>();
for (int i : arr) {
list.add(i);
}
Положительный пример
int[] arr = new int[]{1, 2, 3};
List<Integer> list = new ArrayList<>(arr.length);
for (int i : arr) {
list.add(i);
}
4. Конкатенация строк с помощью StringBuilder
Общее сращивание строк будет оптимизировано в java во время компиляции, но сращивание строк в циклах не может быть оптимизировано во время компиляции java, поэтому его необходимо заменить StringBuilder.Контрпример
String s = "";
for (int i = 0; i < 10; i++) {
s += i;
}
Положительный пример
String a = "a";
String b = "b";
String c = "c";
String s = a + b + c; // 没问题,java编译器会进行优化
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10; i++) {
sb.append(i); // 循环中,java编译器无法进行优化,所以要手动使用StringBuilder
}
5. Произвольный доступ к списку
Разница между массивом и связанным списком: Массивы более эффективны для произвольного доступа. После вызова метода для получения списка, если вы хотите случайным образом получить доступ к данным в нем, но не знаете, является ли внутренняя реализация массива связным списком или массивом, что вам делать? Можно судить, реализует ли он интерфейс RandomAccess.
Положительный пример
// 调用服务获取到list
List<Integer> list = otherService.getList();
if (list instanceof RandomAccess) {
// 内部数组实现,可以随机访问
System.out.println(list.get(list.size() - 1));
} else {
// 内部可能是链表实现,随机访问效率低
}
6. Если вы часто вызываете метод Collection.contains, используйте Set
В библиотеке классов коллекций Java общая временная сложность метода contains списка составляет O (n).Если вам нужно часто вызывать метод contains для поиска данных в коде, вы можете сначала преобразовать список в HashSet, а затем временная сложность O(n) Степень снижается до O(1).
Контрпример
ArrayList<Integer> list = otherService.getList();
for (int i = 0; i <= Integer.MAX_VALUE; i++) {
// 时间复杂度O(n)
list.contains(i);
}
Положительный пример
ArrayList<Integer> list = otherService.getList();
Set<Integer> set = new HashSet(list);
for (int i = 0; i <= Integer.MAX_VALUE; i++) {
// 时间复杂度O(1)
set.contains(i);
}
Сделайте код более элегантным
1. Добавьте заглавную букву L после длинной целочисленной константы.
Контрпример
long value = 1l;
long max = Math.max(1L, 5);
Положительный пример
long value = 1L;
long max = Math.max(1L, 5L);
2. Не используйте магические очки
Использование магических значений может показаться очевидным, когда вы пишете фрагмент кода, но это не так очевидно при отладке. Вот почему магические значения нужно определять как читаемые константы. Однако -1, 0 и 1 не считаются магическими значениями.
Контрпример
for (int i = 0; i < 100; i++){
...
}
if (a == 100) {
...
}
Положительный пример
private static final int MAX_COUNT = 100;
for (int i = 0; i < MAX_COUNT; i++){
...
}
if (count == MAX_COUNT) {
...
}
3. Не используйте реализацию коллекции для назначения статических переменных-членов.
Для статических переменных-членов типа коллекции не используйте реализацию коллекции для присвоения значений, вы должны использовать статические назначения блоков кода.
Контрпример
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");
}
};
Положительный пример
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");
};
4. Удалите неиспользуемые приватные методы и поля
Удалите неиспользуемые закрытые методы и поля, чтобы сделать код чище и проще в обслуживании. Если вам нужно использовать его снова, вы можете получить его из исторической отправки.
Контрпример
public class DoubleDemo1 {
private int unusedField = 100;
private void unusedMethod() {
...
}
public int sum(int a, int b) {
return a + b;
}
}
Положительный пример
public class DoubleDemo1 {
public int sum(int a, int b) {
return a + b;
}
}
5. Вспомогательные классы должны защищать конструкторы
Вспомогательный класс — это набор статических полей и функций, экземпляры которых не должны создаваться. Однако Java добавляет неявный общедоступный конструктор к каждому классу, который не имеет явно определенного конструктора. Поэтому, чтобы избежать неправильного использования java "маленький белый", вы должны явно определить частный конструктор, чтобы скрыть этот неявный общедоступный конструктор.Контрпример
public class MathUtils {
public static final double PI = 3.1415926D;
public static int sum(int a, int b) {
return a + b;
}
}
Положительный пример
public class MathUtils {
public static final double PI = 3.1415926D;
private MathUtils() {}
public static int sum(int a, int b) {
return a + b;
}
}
6. Публичные статические константы должны быть доступны через класс
Хотя доступ к общедоступным статическим константам через экземпляр класса разрешен, легко ввести его в заблуждение, думая, что каждый экземпляр класса имеет общедоступную статическую константу. Следовательно, к общедоступным статическим константам следует обращаться непосредственно через класс.
Контрпример
public class User {
public static final String CONST_NAME = "name";
...
}
User user = new User();
String nameKey = user.CONST_NAME;
Положительный пример
public class User {
public static final String CONST_NAME = "name";
...
}
String nameKey = User.CONST_NAME;
7. Не используйте исключение NullPointerException для оценки нуля
Исключения нулевого указателя следует избегать с помощью кода (например, проверки на ненулевое значение), а не обрабатывать путем перехвата исключений.
Контрпример
public String getUserName(User user) {
try {
return user.getName();
} catch (NullPointerException e) {
return null;
}
}
Положительный пример
public String getUserName(User user) {
if (Objects.isNull(user)) {
return null;
}
return user.getName();
}
8. Используйте String.valueOf(value) вместо ""+value"
При преобразовании других объектов или типов в строки использование String.valueOf(value) более эффективно, чем ""+value.
Контрпример
int i = 1;
String s = "" + i;
Положительный пример
int i = 1;
String s = String.valueOf(i);
9. Добавьте аннотацию @Deprecated к устаревшему коду
Когда часть кода устарела, но не может быть удалена напрямую для совместимости, и вы не хотите, чтобы кто-то использовал ее в будущем, вы можете добавить аннотацию @Deprecated, чтобы пометить ее. Добавьте @deprecated в комментарии к документации, чтобы объяснить и предоставить альтернативы.
Положительный пример
/**
* 保存
*
* @deprecated 此方法效率较低,请使用{@link newSave()}方法替换它
*/
@Deprecated
public void save(){
// do something
}
Не допускайте ошибок в коде
1. Запретить использование конструктора BigDecimal(double)
BigDecimal(double) имеет риск потери точности, что может вызвать исключения бизнес-логики в сценариях точного вычисления или сравнения значений.
Контрпример
BigDecimal value = new BigDecimal(0.1D); // 0.100000000000000005551115...
Положительный пример
BigDecimal value = BigDecimal.valueOf(0.1D);; // 0.1
2. Возвращает пустые массивы и пустые коллекции вместо null
Для возврата null требуется, чтобы вызывающая сторона принудительно обнаружила null , иначе будет сгенерировано исключение нулевого указателя. Возврат пустого массива или пустой коллекции эффективно предотвращает создание вызывающей стороной исключения нулевого указателя, поскольку значение null не обнаружено, а также может удалить инструкцию вызывающей стороны для обнаружения нулевого значения, чтобы сделать код более кратким.
Контрпример
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) {
...
}
}
}
Положительный пример
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. Предпочитайте константы или детерминированные значения для вызова метода equals
Метод equals объекта склонен генерировать исключения нулевого указателя, и вы должны вызывать метод equals с константой или объектом с определенным значением. Конечно, лучше всего использовать метод java.util.Objects.equals().
Контрпример
public void isFinished(OrderStatus status) {
return status.equals(OrderStatus.FINISHED); // 可能抛空指针异常
}
Положительный пример
public void isFinished(OrderStatus status) {
return OrderStatus.FINISHED.equals(status);
}
public void isFinished(OrderStatus status) {
return Objects.equals(status, OrderStatus.FINISHED);
}
4. Поле свойств перечисления должно быть закрытым и неизменяемым.
Перечисления обычно используются как константы.Если в перечислении есть общедоступные поля свойств или заданные методы полей, свойства этих констант перечисления можно легко изменить. В идеале поля свойств в перечислении приватные и назначаются в приватном конструкторе, нет соответствующего метода Setter, лучше добавитьконечный модификатор.
Контрпример
public enum UserStatus {
DISABLED(0, "禁用"),
ENABLED(1, "启用");
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;
}
}
Положительный пример
public enum UserStatus {
DISABLED(0, "禁用"),
ENABLED(1, "启用");
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;
}
}
5. Будьте осторожны с String.split(String regex)
Метод разделения String, входящая строка с разделителями является регулярным выражением! Некоторые ключевые слова (например, .|и т.д.) нужно экранировать.
Контрпример
"a.ab.abc".split("."); // 结果为[]
"a|ab|abc".split("|"); // 结果为["a", "|", "a", "b", "|", "a", "b", "c"]
Положительный пример
"a.ab.abc".split("\\."); // 结果为["a", "ab", "abc"]
"a|ab|abc".split("\\|"); // 结果为["a", "ab", "abc"]