10 ОСНОВНЫХ ОШИБОК JAVA-РАЗРАБОТЧИКОВ

В этом списке перечислены 10 основных ошибок, которые часто допускают разработчики Java.

№1. Преобразовать массив в ArrayList

Чтобы преобразовать массив в ArrayList, разработчики часто делают это:

List<String> list = Arrays.asList(arr);

Arrays.asList() вернет ArrayList, который является закрытым статическим классом внутри Arrays, а не классом java.util.ArrayList. Класс java.util.Arrays.ArrayList имеет методы set(), get(), contains(), но не имеет методов добавления элементов, поэтому его размер фиксирован. Чтобы создать настоящий ArrayList, вы должны сделать:

ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(arr));

Конструктор ArrayList может принимать тип Collection, который также является супертипом для java.util.Arrays.ArrayList.

№2. Проверить, содержит ли массив значение

Разработчики часто делают:

Set<String> set = new HashSet<String>(Arrays.asList(arr));
return set.contains(targetValue);

Код работает, но нет необходимости сначала преобразовывать список в set. Преобразование списка в набор требует дополнительного времени. Это может быть так же просто, как:

Arrays.asList(arr).contains(targetValue);

or

for(String s: arr){
	if(s.equals(targetValue))
		return true;
}
return false;

Первый более читабелен, чем второй.

№3. Удалить элемент из списка внутри цикла

Рассмотрим следующий код, который удаляет элементы во время итерации:

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
for (int i = 0; i < list.size(); i++) {
	list.remove(i);
}
System.out.println(list);

Вывод:

[b, d]

В этом методе есть серьезная проблема. Когда элемент удаляется, размер списка уменьшается, а индекс изменяется. Поэтому, если вы хотите удалить несколько элементов внутри цикла с помощью индекса, это не сработает должным образом.

Возможно, вы знаете, что использование итератора — это правильный способ удаления элементов внутри циклов, и вы знаете, что каждый цикл в Java работает как итератор, но на самом деле это не так. Рассмотрим следующий код:

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
 
for (String s : list) {
	if (s.equals("a"))
		list.remove(s);
}

Он выбросит исключение concurrentmodificationexception.

Вместо этого допустимо следующее:

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
	String s = iter.next();
 
	if (s.equals("a")) {
		iter.remove();
	}
}

.next() нужно вызывать перед .remove(). В цикле foreach компилятор вызовет .next() после операции удаления элемента, вызвавшей ошибку ConcurrentModificationException. Вы можете взглянуть на исходный код ArrayList.iterator().

№ 4. Hashtable и HashMap

По соглашению в алгоритме Hashtable — это имя структуры данных. Но в Java структура данных называется HashMap. Одно из ключевых различий между Hashtable и HashMap заключается в том, что Hashtable синхронизировано. Так что очень часто вам не нужно Hashtable, вместо этого следует использовать HashMap.

HashMap, TreeMap, Hashtable, LinkedHashMap
10 основных вопросов о Map

№ 5. Использовать необработанный тип коллекции

В Java необработанный тип и неограниченный подстановочный знак легко смешиваются. Возьмем, к примеру, Set, Set — необработанный тип, а Set<?> — неограниченный подстановочный знак.

Рассмотрим следующий код, в котором в качестве параметра используется необработанный тип List:

public static void add(List list, Object o){
	list.add(o);
}
public static void main(String[] args){
	List<String> list = new ArrayList<String>();
	add(list, 10);
	String s = list.get(0);
}

Этот код вызовет исключение:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
	at ...

Использование коллекции необработанных типов опасно, поскольку коллекции необработанных типов пропускают проверку универсального типа и небезопасны. Существуют огромные различия между Set, Set<?> и Set<Object>. Сравните
необработанный текст с неограниченным подстановочным знаком и стиранием текста.

№ 6. Уровень доступа

Очень часто разработчики используют public для поля класса. Легко получить значение поля путем прямой ссылки, но это очень плохой дизайн. Эмпирическое правило заключается в том, чтобы предоставить участникам как можно более низкий уровень доступа.

публичный, по умолчанию, защищенный и частный

№ 7. ArrayList и LinkedList

Когда разработчики не знают разницы между ArrayList и LinkedList, они часто используют ArrayList, потому что это выглядит знакомо. Однако между ними существует огромная разница в производительности. Короче говоря, LinkedList следует предпочесть, если имеется большое количество операций добавления/удаления и не так много операций произвольного доступа. Проверьте ArrayList и LinkedList, чтобы получить больше информации об их производительности, если это для вас в новинку.

№8. Изменяемый и неизменяемый

Неизменяемые объекты имеют много преимуществ, таких как простота, безопасность и т. д. Но для каждого отдельного значения требуется отдельный объект, а слишком большое количество объектов может привести к высокой стоимости сборки мусора. При выборе между изменяемым и неизменяемым должен быть баланс.

Как правило, изменяемые объекты используются, чтобы не создавать слишком много промежуточных объектов. Одним из классических примеров является объединение большого количества строк. Если вы используете неизменяемую строку, вы создадите множество объектов, которые сразу же подлежат сборке мусора. Это тратит время и энергию на ЦП, используя изменяемый объект правильное решение (например, StringBuilder).

String result="";
for(String s: arr){
	result = result + s;
}

Есть и другие ситуации, когда желательны изменяемые объекты. Например, передача изменяемых объектов в методы позволяет вам собирать несколько результатов, не прыгая через слишком много синтаксических обручей. Другим примером является сортировка и фильтрация: конечно, вы можете создать метод, который берет исходную коллекцию и возвращает отсортированную, но это будет чрезвычайно расточительно для больших коллекций. (Из ответа dasblinkenlight на Stack Overflow)

Почему строка неизменяема?

№ 9. Конструктор Super и Sub

Эта ошибка компиляции возникает из-за того, что суперконструктор по умолчанию не определен. В Java, если класс не определяет конструктор, компилятор по умолчанию вставит конструктор без аргументов для класса по умолчанию. Если конструктор определен в классе Super, в данном случае Super(String s), компилятор не будет вставлять конструктор по умолчанию без аргументов. Это ситуация для Супер класса.

Конструкторы класса Sub, как с аргументами, так и без аргументов, будут вызывать конструктор Super без аргументов. Поскольку компилятор пытается вставить super() в 2 конструктора в классе Sub, но конструктор Super по умолчанию не определен, компилятор сообщает об ошибке.

Чтобы решить эту проблему, просто

1) добавьте конструктор Super() в класс Super, например

public Super(){
    System.out.println("Super");
}

2) удалить самоопределяемый конструктор Super

3) добавить super(value) в подконструкторы.

№10. "" или конструктор?

Строку можно создать двумя способами:

//1. use double quotes
String x = "abc";
//2. use constructor
String y = new String("abc");

В чем разница?

Следующие примеры могут дать быстрый ответ:

String a = "abcd";
String b = "abcd";
System.out.println(a == b);  // True
System.out.println(a.equals(b)); // True
 
String c = new String("abcd");
String d = new String("abcd");
System.out.println(c == d);  // False
System.out.println(c.equals(d)); // True