предисловие
Недавно, когда мы сканировали код с помощью сонара, мы обнаружили много проблем. В дополнение к обычным ошибкам и дырам в безопасности, есть несколько ошибок использования методов, которые меня очень интригуют. Почему меня так интересуют эти методы? Поскольку они настолько сбивают с толку, они могут сбить нас с толку.
1. replace заменит все символы?
Много раз, когда мы используем строки, мы хотим поместить символы в строки, такие как: ATYSDFA*YA
заменить на символыB
, первой мыслью может быть использованиеreplace
метод.
Если вы хотите иметь всеA
заменены наB
, очевидно, его можно использоватьreplaceAll
метод, так как он очень интуитивно понятен, вы можете догадаться о его назначении из названия метода.
Итак, возникает вопрос:replace
Будет ли метод заменять все совпадающие символы?
Официальный jdk дал ответ.
Этот метод заменяет каждую совпадающую строку.
теперь, когдаreplace
а такжеreplaceAll
может заменить все совпадающие символы, так в чем же между ними разница?
-
replace
Есть два перегруженных метода.
Один из параметров метода: char oldChar и char newChar, поддерживает замену символов.
source.replace('A', 'B')
Параметры другого метода: цель CharSequence и замена CharSequence, которая поддерживает замену строки.
source.replace("A", "B")
-
replaceAll
Параметры метода: регулярное выражение строки и замена строки, замена на основе регулярного выражения.
Обычная замена строки:
source.replaceAll("A", "B")
Замена регулярного выражения (замените * на C):
source.replaceAll("\\*", "C")
Кстати, заменив * на C, используйтеreplace
метод также может быть реализован:
source.replace("*", "C")
Специальные символы не нужно экранировать.
Однако будьте осторожны, чтобы не использовать следующие обозначения:
source.replace("\\*", "C")
Такой способ записи сделает строку незаменимой.
Еще один небольшой вопрос: что, если я хочу заменить только первую совпадающую строку?
В это время вы можете использоватьreplaceFirst
метод:
source.replaceFirst("A", "B")
2. Integer не может использовать == для оценки равенства?
Не знаю, видели ли вы это в проекте, некоторые коллегиInteger
Использование двух параметров типа==
Сравнения равны?
Я все равно это видел, так правильно ли это использование?
Мой ответ заключается в том, что это зависит от конкретной ситуации, и я не могу сказать, правильно ли это или неправильно.
Некоторые поля состояния, такие как:orderStatus
Есть: -1 (не заказано), 0 (заказано), 1 (оплачено), 2 (выполнено), 3 (отменено), 5 состояний.
В это время, если вы используете==
Проверить на равенство:
Integer orderStatus1 = new Integer(1);
Integer orderStatus2 = new Integer(1);
System.out.println(orderStatus1 == orderStatus2);
Результат возврата будетtrue
?
ответfalse
.
Некоторые учащиеся могут возразить,Integer
Нет диапазона, в котором находится:-128
-127
кеш?
Зачемfalse
?
Сначала взгляните на конструктор Integer:
На самом деле он не использует кеш.
Так где же используется кеш?
ответvalueOf
В методе:
Если вышеуказанное решение изменить на это:
String orderStatus1 = new String("1");
String orderStatus2 = new String("1");
System.out.println(Integer.valueOf(orderStatus1) == Integer.valueOf(orderStatus2));
Результат возврата будетtrue
?
Ответ: правдаtrue
.
Мы должны развивать хорошие привычки кодирования и использовать как можно меньше==
судить двоихInteger
Равенство данных типа равно только в приведенных выше особых сценариях.
Вместо этого используйтеequals
Оценка метода:
Integer orderStatus1 = new Integer(1);
Integer orderStatus2 = new Integer(1);
System.out.println(orderStatus1.equals(orderStatus2));
3. Использовать BigDecimal без потери точности?
Обычно мы определяем некоторые поля десятичного типа (например, количество) как BigDecimal вместо Double, чтобы избежать потери точности.
Может быть такой сценарий при использовании Double:
double amount1 = 0.02;
double amount2 = 0.03;
System.out.println(amount2 - amount1);
Обычно ожидаетсяamount2 - amount1
должно быть равно0.01
Но результат выполнения:
0.009999999999999998
Фактические результаты оказались меньше ожидаемых.
Вычитание двух параметров типа Double будет преобразовано в двоичное, потому что количество значащих цифр Double равно16
В этом случае будет не хватать десятичных знаков для хранения, и в этом случае будут возникать ошибки.
Здравый смысл подсказывает нам, что использование BigDecimal позволяет избежать потери точности.
Но позволяет ли использование BigDecimal избежать потери точности?
ответ отрицательный.
Почему?
BigDecimal amount1 = new BigDecimal(0.02);
BigDecimal amount2 = new BigDecimal(0.03);
System.out.println(amount2.subtract(amount1));
В этом примере определяются два параметра типа BigDecimal, данные инициализируются с помощью конструктора, а затем печатается значение после вычитания двух параметров.
результат:
0.0099999999999999984734433411404097569175064563751220703125
Ненаучно, почему точность все еще теряется?
Вот такое описание метода построения BigDecimal в jdk:
Грубо говоря, результат этого конструктора может быть непредсказуемым, и могут быть случаи, когда он равен 0,1 при создании, но на самом деле равен 0,10000000000000000055511151231257827021181583404541015625.
Видно, что при инициализации объекта с помощью конструктора BigDecimal также теряется точность.
Так как же не потерять точность?
BigDecimal amount1 = new BigDecimal(Double.toString(0.02));
BigDecimal amount2 = new BigDecimal(Double.toString(0.03));
System.out.println(amount2.subtract(amount1));
использоватьDouble.toString
Метод преобразует десятичное число типа double, чтобы не потерять точность.
На самом деле есть лучший способ:
BigDecimal amount1 = BigDecimal.valueOf(0.02);
BigDecimal amount2 = BigDecimal.valueOf(0.03);
System.out.println(amount2.subtract(amount1));
использоватьBigDecimal.valueOf
Метод инициализирует параметр типа BigDecimal, который также может гарантировать, что точность не будет потеряна. В новой версии руководства по разработке Alibaba также рекомендуется использовать этот метод для создания параметров BigDecimal.
4. Нельзя использовать String для конкатенации строк?
String
Тип строки называется неизменяемой последовательностью, что означает, что данные объекта не могут быть изменены после его определения.Если вы хотите изменить его, вам нужно создать новый объект.
String a = "123";
String b = "456";
String c = a + b;
System.out.println(c);
В сцене с большим количеством конкатенаций строк, если объект определен какString
тип, будет генерировать много бесполезных промежуточных объектов, тратить место в памяти и будет иметь низкую эффективность.
В этом случае мы можем использовать более эффективную последовательность переменных символов:StringBuilder
а такжеStringBuffer
, чтобы определить объект.
Так,StringBuilder
а такжеStringBuffer
Какая разница?
StringBuffer добавлен к каждому основному методуsynchronized
ключевое слово, которого нет в StringBuilder. Таким образом, StringBuffer является потокобезопасным, а StringBuilder — нет.
На самом деле у нас редко бывают сценарии, в которых нам нужно объединить строки в нескольких потоках, поэтому StringBuffer используется очень редко. Как правило, при объединении строк мы рекомендуем использоватьStringBuilder
, через егоappend
Метод добавляет строку, генерирует только один объект и не блокируется, что более эффективно.
String a = "123";
String b = "456";
StringBuilder c = new StringBuilder();
c.append(a).append(b);
System.out.println(c);
Далее следует ключевой вопрос:Сращивание строк с использованием объектов типа String должно быть менее эффективным, чем объекты типа StringBuilder?
ответ отрицательный.
Почему?
использоватьjavap -c StringTest
Команда для декомпиляции:
Из рисунка видно, что определены два параметра типа String и еще один параметр типа StringBuilder, а затем дважды используется метод append для добавления строки.
Если код такой:
String a = "123";
String b = "789";
String c = a + b;
System.out.println(c);
использоватьjavap -c StringTest
Что будет результатом декомпиляции команды?
Мы были удивлены, обнаружив, что также были определены два параметра типа String и еще один параметр типа StringBuilder, а затем дважды использовался метод append для добавления строки. Тот же результат, что и выше.
** Фактически, начиная с jdk5, java оптимизировала операцию + строк типа String.После того, как операция скомпилирована в файл байт-кода, она будет оптимизирована как операция добавления StringBuilder.
5. Разница между isEmpty и isBlank
Когда мы работаем со строками, нам часто нужно определить, пуста ли строка. Без помощи каких-либо инструментов мы обычно судим следующим образом:
if (null != source && !"".equals(source)) {
System.out.println("not empty");
}
Однако каждый раз будет немного сложно судить, так как многие пакеты jar инкапсулируют пустую строку. В настоящее время на рынке представлены следующие основные инструменты:
- StringUtils весной
- StringUtils в jdbc
- StringUtils в apache common3
ноspring
серединаStringUtils
только классisEmpty
метод, нетisNotEmpty
метод.
jdbc
серединаStringUtils
только классisNullOrEmpty
метод, ниisNotNullOrEmpty
метод.
Так что очень рекомендую сюдаapache common3
серединаStringUtils
класс, который содержит множество практичных нулевых методов:isEmpty
,isBlank
,isNotEmpty
,isNotBlank
И т. д., есть и другие методы манипулирования строками.
Вот проблема,isEmpty
а такжеisBlank
Какая разница?
использоватьisEmpty
Оценка метода:
StringUtils.isEmpty(null) = true
StringUtils.isEmpty("") = true
StringUtils.isEmpty(" ") = false
StringUtils.isEmpty("bob") = false
StringUtils.isEmpty(" bob ") = false
использоватьisBlank
Оценка метода:
StringUtils.isBlank(null) = true
StringUtils.isBlank("") = true
StringUtils.isBlank(" ") = true
StringUtils.isBlank("bob") = false
StringUtils.isBlank(" bob ") = false
Основное различие между этими двумя методами заключается в том, что это" "
В случае пустой строкиisNotEmpty
возвращает ложь, аisBlank
Вернуть истину.
6. Должен ли результат запроса картографа быть пустым?
Когда я однажды просматривал код, коллега сказал, что пустое предложение здесь можно удалить, что освежило мою память:
List<User> list = userMapper.query(search);
if(CollectionUtils.isNotEmpty(list)) {
List<Long> idList = list.stream().map(User::getId).collect(Collectors.toList());
}
Потому что, согласно здравому смыслу, коллекция, запрашиваемая общим вызовом метода, может бытьnull
, который должен быть нулевым. Впрочем, здесь он особенный, я проверилmybatis
Исходный код, этот пустой код действительно можно удалить.
Что происходит?
mybatis
Метод запроса в итоге будет передан методу handleResultSets класса DefaultResultSetHandler:
Этот метод возвращаетmultipleResults
Список объектов коллекции в начале методаnew
Выходите, он точно не будет пустым.
Так что не удивляйтесь, если увидите, что кто-то использует результат запроса прямо в коде проекта, не удивляйтесь:
List<User> list = userMapper.query(search);
List<Long> idList = list.stream().map(User::getId).collect(Collectors.toList());
потому чтоmapper
Нижний слой обработан, он не будет вызывать исключение нулевого указателя.
7. Правильное использование метода indexOf
Когда я просматривал чужой код, я увидел местоindexOf
Используя этот способ письма, я больше впечатлен:
String source = "#ATYSDFA*Y";
if(source.indexOf("#") > 0) {
System.out.println("do something");
}
Вы, ребята, говорите, что этот код будет распечатанdo something
?
ответ отрицательный.
Зачем?
JDK официально заявил, что если его не существует, он вернет -1
indexOf
Метод возвращает позицию указанного элемента в строке, начиная с 0. в то время как приведенный выше пример#
в первой позиции строки, поэтому вызовитеindexOf
Значение после метода на самом деле равно 0. Итак, условие ложно и не будет напечатаноdo something
.
Если вы хотите пройтиindexOf
При определении того, существует ли элемент, используйте:
if(source.indexOf("#") > -1) {
System.out.println("do something");
}
На самом деле есть более изящныеcontains
метод:
if(source.contains("#")) {
System.out.println("do something");
}
Последнее слово (пожалуйста, обратите внимание, не проституируйте меня по пустякам)
Если эта статья полезна или вдохновляет вас, пожалуйста, помогите обратить внимание, ваша поддержка является самой большой мотивацией для меня, чтобы продолжать писать.
Попросите в один клик три ссылки: лайк, вперед и смотреть.
Следите за официальной учетной записью: [Су Сан сказал о технологии], ответьте в официальной учетной записи: интервью, артефакты кода, руководства по разработке, управление временем имеют большие преимущества для поклонников, и ответьте: присоединяйтесь к группе, вы можете общаться и учиться со многими старшими из BAT. производители .