10 самых распространенных ошибок Java-разработчиков

Java

Я недавно изучаю Java, и я думаю, что эта старая статья хороша, поэтому я перевел ее, и я чувствую, что она полезна для новичков.

оригинал:woohoo.program Creek.com/2014/05/top…

Консультант: Чжан Бо (один из разработчиков Gradle)

Перевод начинается


Эти 10 ошибок основаны на моем анализе проектов на GitHub, вопросах и ответах на StackOverflow и трендах ключевых слов в поиске Google.

1 Ошибка преобразования массива в ArrayList

Некоторые разработчики часто используют такой код для преобразования Array в ArrayList.

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

Возвращаемое значение Arrays.asList() — это объект класса ArrayList, который является закрытым статическим классом (java.util.Arrays.ArrayList) в классе Arrays, а не в классе java.util.ArrayList.

java.util.Arrays.ArrayList имеет методы set() / get() / contains(), но не предоставляет никакого метода для добавления элементов, поэтому его длина фиксирована. Если вы хотите получить экземпляр класса java.util.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);

Вышеприведенный код может работать, но нет необходимости преобразовывать список в набор, что является пустой тратой времени.Простой способ написать это так:

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

или что-то вроде этого

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]

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

Вы можете подумать, что правильный способ удаления элементов в цикле — это итератор, например, цикл foreach выглядит как итератор, но это проблема.

Рассмотрим следующий код (код 1):

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

Будет выброшено исключение ConcurrentModificationException.

Чтобы правильно удалять элементы при обходе, вы должны написать это (листинг 2):

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 в листинге 1 компилятор вызовет .next() после удаления элемента, что приведет к исключению ConcurrentModificationException. Если вы хотите копнуть глубже, вы можетеВзгляните на исходный код ArrayList.iterator()..

4 Используйте Hashtable или HashMap

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

5 Ошибка при прямом использовании примитивного типа Collection

В Java «необработанные типы» и «неограниченные подстановочные типы» легко спутать. Например, 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>Существуют очень большие различия междуSet vs. Set<?>"и"Java Type Erasure Mechanism".

6 Установлен слишком высокий уровень доступа

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

Видеть "public, default, protected, and private".

7 Ошибка выбора ArrayList и LinkedList

Не понимая разницы между ArrayList и LinkedList, легко склониться к использованию ArrayList, потому что он выглядит более распространенным.

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

Если вы хотите глубже изучить эти различия в производительности, ознакомьтесь сArrayList vs. LinkedList vs. Vector".

8 Изменяемый или неизменный?

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

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

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

Есть также ситуации, когда стоит использовать изменяемые объекты. Например, вы можете обойти ограничения синтаксиса, передав изменяемый объект методу для сбора нескольких результатов. Другой пример — операции сортировки и фильтрации, хотя вы можете вернуть новый отсортированный объект, но если количество элементов велико, это будеттратить много памяти.

Дальнейшее чтение "почему строки неизменяемы".

9 Конструкторы суперкласса и подкласса

class Super {
  String s;
  public Super(String s){
    this.s = s;
  }
}

public class Sub extend Super{
  int x = 200;
  public Sub(String s){ // 编译错误
  }
  public Sub(){  // 编译错误
    System.out.println("Sub");
  }
  public static void main(String[] args){
    Sub s = new Sub();
  }
}

Приведенный выше код будет иметь ошибку компиляции, поскольку конструктор Super() не реализован. В Java, если класс не определяет конструктор, компилятор вставит конструктор по умолчанию без аргументов. Но если бы в классе Super уже был конструктор Super(String s), то компилятор не стал бы вставлять этот конструктор без параметров по умолчанию. Это то, что происходит с приведенным выше кодом.

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

Есть три способа решить эту проблему:

  1. Добавьте конструктор без параметров Super() в класс Super.
  2. Удалить параметризованный конструктор в классе Super
  3. Добавить супер(значение) в конструктор подкласса

Для получения более подробной информации см. "Constructors of Sub and Super Classes in Java?".

10 Используйте "" или конструктор

Строки могут быть построены двумя способами:

// 1. 使用双引号
String x = "abd";
// 2. 使用构造函数
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

Чтобы понять, как строки, сгенерированные этими двумя методами, существуют в памяти, вы можете увидеть "Создать строку Java с помощью « » или конструктора?

Суммировать

Эти 10 ошибок основаны на моем анализе проектов на GitHub, вопросах и ответах на StackOverflow и трендах ключевых слов в поиске Google. Возможно, они не входят в десятку самых популярных ошибок, но они довольно распространены. Если у вас есть какие-либо возражения, вы можете оставить мне сообщение. Я был бы очень признателен, если бы вы могли указать мне другие распространенные ошибки.