В этой статье мы рассмотрим подборку или даже рейтинг 10 ошибок, которые чаще всего делают начинающие Java разработчики. Подборка включает в себя ошибки связанные с использованием типов данных в Java, утилитных методов для обработки массивов и коллекций, использованием ООП в Java, строками и другими немаловажными темами.
Неправильное преобразование массива в ArrayList
Чаще всего это выглядит так:
1 |
List<String> list = Arrays.asList(array); |
Дальше идет работа с этим списком: попытки добавления/удаления элементов, обновление уже существующих т.д.
В такой ситуации следует помнить, что список, который возвращает метод Arrays.asList() является внутренним статическим классом внутри класса Arrays
( класс java.util.Arrays.ArrayList
) и, как видите, это не экземпляр java.util.ArrayList
класса. В экземпляра java.util.Arrays.ArrayList
класса есть методы set()
, get()
, contains()
методы и нет возможностей добавления элементов, потому что его размер фиксируется.
Правильным выходом в данной ситуации будет создание экземпляра java.util.ArrayList
класса. Сделать это можно следующим образом:
1 |
List<String> safeList = new ArrayList<String>(Arrays.asList(array)); |
Отдельно хочу отметить, что это одна из самых популярных ошибок среди начинающих программистов. Она появляется по причине того, что человек начал изучать SDK, поверхностно ознакомился с методами для обработки массивов, нашел интересующий его метод, но не вник в детали того как он работает и делает ли он вообще то, что от него ожидают. Такой подход чаще всего встречается среди начинающих, которые в короткие строки перерабатывают множество новой информации по Java. Но ради справедливости стоит отметить, что и более опытные разработчики часто грешат этим.
Подробнее о работе с массивами читайте здесь. А о самом методе Arrays.asList() тут.
Сложная проверка на то, содержит ли массив значение
Еще одна интересная ошибка, которую постоянно допускают новички. Давайте рассмотрим на примере популярный вариант проверки:
1 2 3 4 |
private boolean containsValue(String[] array, String whatToCheck) { Set<String> set = new HashSet<String>(Arrays.asList(array)); return set.contains(whatToCheck); } |
Как видите, у нас есть метод для проверки того, содержит ли массив какой-то элемент. Его суть сводится к тому, что мы оборачиваем входной массив в HashSet и пользуемся стандартными методами коллекции для поиска элемента. Удобно ведь — все так делали 🙂 Такой метод работать будет, но работать дольше, чем другие способы, да и писать его дольше.
Нам не нужно конвертировать список во множество лишь для того, чтобы использовать удобный метод. Да и сама конвертация займет дополнительное время.
Лучшим выходом в данной ситуации я бы назвал использование такого способа:
1 |
Arrays.asList(array).contains(whatToCheck); |
Как видите, подход похож, но записывается всего в 1 строку. Или вспомнить самый простой и надежный способ:
1 2 3 4 5 6 7 |
... for(String string: array) { if(string.equals(whatToCheck)) { return true; } } return false; |
Если бы выбирать из этих двух способов проверки содержит ли массив значение, то я бы выбрал первый — за удобство и лаконичность. А что используете Вы в такой ситуации? Пишите в комментариях.
Попытка удалить элемент во время обхода списка
Рассмотрим пример кода, в котором происходит попытка удалить элемент во время обхода элементов списка:
1 2 3 4 5 6 7 |
List<String> listOfStrings = new ArrayList<String>(Arrays.asList("z", "x", "c", "v")); for (int i = 0; i < listOfStrings.size(); i++) { if (listOfStrings.get(i).equals("x")) { listOfStrings.remove(i); } } System.out.println(listOfStrings.toString()); |
В ходе выполнения этого кода будет выброшено ConcurrentModificationException
, так как нельзя удалять элементы во время итерирования по списку. Такая же ситуация будет и с for-each циклом. Удалять элементы безопасно можно только при использовании интерфейса Iterator, например, так:
1 2 3 4 5 6 7 8 9 |
List<String> listOfStrings = new ArrayList<String>(Arrays.asList("z", "x", "c", "v")); Iterator<String> iterator = listOfStrings.iterator(); while (iterator.hasNext()) { String s = iterator.next(); if (s.equals("x")) { iterator.remove(); } } |
Подробнее об исключении ConcurrentModificationException читайте здесь.
Всегда используют ArrayList и не знают где использовать LinkedList
Когда разработчик не знает разницы между ArrayList
и LinkedList
, то чаще всего предпочтение отдает ArrayList
— это ведь просто и знакомо. Тем не менее, существует огромная разница в производительности между этими коллекциями. Если вкратце, то LinkedList
желательно использовать, если имеется большое количество операции добавления или удаления. Лучше об этой коллекции и других читайте в обзорной статье Коллекции в Java.
Не знают в чем разница между изменяемостью или неизменяемостью объектов в Java
Неизменяемые объекты имеют ряд преимуществ перед изменяемыми, например, простота и безопасность. Однако есть и недостатки: они требуют отдельного объекта для каждого нового значения, а много объектов может привести к высокой стоимости сбора мусора. Поэтому всегда нужно держать баланс при выборе между изменяемыми и неизменяемыми объектами.
Если вкратце, то изменяемые объекты используются для того, чтобы избежать большого количества промежуточных объектов. Отличным примером будет конкатенация большого количества строк: если вы используете строку, то по ходу дела создается много объектов, которые должны немедленно отправиться на сборку мусора. Это напрасно тратит время процессора, поэтому правильным будет решение со StringBuilder
.
Подробнее об этой теме читайте в статьях: ТОП-10 вопросов по строкам, Почему строки неизменные?.
Не знают разницу между созданием строк с использованием конструктора и двойных кавычек («»)
Строки в Java можно создавать с помощью конструктора, например, с помощью двойных кавычек («»):
1 2 3 4 |
// с использованием двойных кавычек String x = "Java"; // с использованием конструктора String y = new String("Java"); |
Казалось бы, эти строки полностью одинаковы, но есть ряд нюансов, о которых часто забывают начинающие Java разработчики. Смотрим на примере:
1 2 3 4 5 6 7 8 9 |
String stringOne = "Java"; String stringTwo = "Java"; System.out.println(stringOne == stringTwo); // получим true System.out.println(stringOne.equals(stringTwo)); // тоже получим true String stringThree = new String("Java"); String stringFour = new String("Java"); System.out.println(stringThree == stringFour); // получим false System.out.println(stringThree.equals(stringFour)); // получим true |
Понять почему происходит так, а не иначе Вам поможет статья Что такое пул строк в Java? и Почему строки в Java неизменные.
Не знают разницу между Hashtable или HashMap?
Одним из ключевых отличий Hashtable
и HashMap
является то, что Hashtable
синхронизирована. Поэтому в большинстве случаев нужно использовать HashMap
, а не Hashtable
. Подробно об этом я писал в отдельной статье.
Не знают разницу между Set или Set<?>
Программисты, которые только начинают свой путь в Java, не знают разницу между Set и Set<?>. Это касается и других коллекций. Подробнее в этой статье.
Не используют правильные уровни доступа
Очень часто разработчики используют модификатор доступа public для полей класса. Да, в этом случае очень удобно и легко получить значение поля, но это считается плохим тоном в разработке на Java. Давайте полям класса уровень доступа как можно ниже.
На этом моя подборка заканчивается. Если у Вас есть что добавить к популярным ошибкам начинающих (и не очень) Java разработчиков, жду Вас в комментариях :).
У меня пример с попыткой удаления элемента при обходе списка не выбросил исключение. Исключение выбрасывается только когда явно использую итератор, как в примере по ссылке.
Вообще пример с попыткой получения ошибки ConcurrentModificationException при удалении элемента во время обхода списка некорректен. Чтобы получить эту ошибку нужно либо использовать итератор в явном виде, либо использовать цикл в стиле foreach. На stackoverflow разбирался вопрос почему не возникала ошибка при удалении элемента даже в случае использования итератора — оказывается, если удаляемый элемент в единственном числе и предпоследний, то все отработает нормально 🙂
Так что имхо лучше заменить пример и добавить, что в документации сказано дословно «для удаления элементов из коллекций в циклах использовать итератор». Как вариант, понравилось решение:
List listOfStrings = new ArrayList(Arrays.asList(«z», «x», «c», «v»));
listOfStrings.removeIf(s -> s.equals(«x»));
System.out.println(listOfStrings.toString());
Для этой конструкции подойдёт только android N