JavaRush /Java блог /Random UA /10 помилок, що часто допускаються Java розробниками
theGrass
24 рівень
Саратов

10 помилок, що часто допускаються Java розробниками

Стаття з групи Random UA
10 помилок, що часто допускаються Java розробниками - 1
Цей список включає 10 помилок, що часто допускаються Java розробниками.
  1. Перетворення масиву в ArrayList .

    Для перетворення масиву в ArrayList розробники часто використовують такий спосіб:

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

    Arrays.asList()поверне об'єкт класу ArrayListякий є внутрішнім приватним статичним класом (private static class)класу Arrays, а це не клас. java.util.ArrayList.Клас java.util.Arrays.ArrayListмістить методи set(), get(), contains(), але не містить жодних методів для додавання елементів, його розмір фіксований . Для створення сьогодення java.util.ArrayListслід зробити так:

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

    Конструктор класу java.util.ArrayListможе приймати як параметр всі об'єкти реалізують інтерфейс Collection, реалізацію якого успадкував і класjava.util.Arrays.ArrayList

    (private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable).

  2. Перевіряє масив на наявність конкретного значення.

    Розробники часто роблять так:

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

    Код виходить робочий, але немає необхідності перетворювати Listна Set. Перетворення Setзайме додатковий час. Насправді все просто:

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

    або

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

    Перший спосіб значно коротший.

  3. Видалення елемента з Listциклу

    Розглянемо наступний код, який видаляє елементи в циклі:

    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]

    Виходить серйозна помилка. Коли елемент видалено, розмір Listзменшується та індекси елементів змінюються.

    Так що відмовтеся від такого способу, якщо ви хочете видалити кілька елементів у циклі, використовуючи індекс.

    Можливо ви знаєте, що використання ітератора правильне рішення для видалення елементів у циклі, і ви знаєте, що цикл в стилі for-eachпрацює як ітератор, але це не так.

    Розглянемо наступний код:

    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().

    У циклі в стилі for-eachкомпілятор викличе метод remove(), а вже потім next(), що викличе помилку ConcurrentModificationException. Ви можете переглянути код ArrayList.iterator().

  4. Hashtableпроти HashMap.

    Через відповідну реалізацію, Hashtable — це назва структури даних.

    Але в Java назва для структури даних це HashMap. Одне з ключових відмінностей між Hashtableі HashMapце те, що Hashtableсинхронізовано.Так що не варто використовувати Hashtableтам де більше підходить HashMap.

    HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMap .

    10 основних питань про інтерфейс Map

  5. Використання колекцій без обмежень за вмістом

    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>почитайте ось це посилання .

  6. Рівень доступу

    Найчастіше для полів класу розробники використовують модифікатор доступу public. Так простіше отримати значення поля безпосередньо. Правильніше використовувати якомога обмеженіший доступ до членів класу.

    public, default, protected, and private .

  7. ArrayListпротиLinkedList

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

  8. Mutable(Змінний) проти Immutable(Незмінний)

    Незмінний об'єкт має багато переваг: простота, безпека тощо. Але він вимагає окремого об'єкта для кожного нового значення, і за дуже багато об'єктів доведеться заплатити зниженням продуктивності. Повинен бути баланс при виборі між об'єктом, що змінюється і незмінним.

    В основному щоб уникнути підготовки проміжних об'єктів використовується об'єкт, що змінюється. Один із класичних прикладів це конкатенація великої кількості рядків. Якщо ви використовуєте незмінний об'єкт типу String, то ви створюєте багато об'єктів, які відразу потраплять до збирача сміття. Це витрачає час і енергію процесора, тому правильне рішення це використання об'єктів, що змінюються (наприклад StringBuilder).

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

    Ще один приклад використання об'єктів, що змінюються - передача такого об'єкта в метод. Це дозволить повернути результат у ньому без створення зайвих об'єктів. Працюючи з об'єктами великого розміру, наприклад колекціями, передача колекції в спосіб сортування і повернення результату до іншої колекції призводить до зайвої витрати ресурсів. ( Відповідь користувача dasblinkenlight на Stack Overflow ).

    Чому об'єкт класу String незмінний?

  9. Конструктори класів SuperтаSub

    10 помилок, що часто допускаються Java розробниками - 2

    Ця помилка компіляції викликана тим, що у класу-предка не визначено конструктора за умовчанням. У Java , якщо ви самі не опишите конструктор класу, компілятор сам створить конструктор за замовчуванням, який не вимагає жодних аргументів. Якщо ж конструктор описаний у класі , Superяк Super(String s){}компілятор сам нічого додавати не буде. Що ми й спостерігаємо у нашому прикладі.

    Конструктор класу Sub, не важливо який з них, викличе конструктор за умовчанням класу Super, оскільки не вказано нічого іншого. Оскільки конструктора за умовчанням у класі Superнемає, це призведе до помилки компіляції.

    Перший варіант вирішення цієї проблеми - додати конструктор за замовчуванням до класуSuper

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

    Другий варіант - видаліть описаний нами конструктор із класу Super, щоб компілятор створив за замовчуванням конструктор.

    І останній варіант - додайте виклик super(value)у конструктори класу Sub, щоб замість конструктора за замовчуванням викликався конструктор класуSuper

    Constructor of Super and Sub

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

    Є два способи створити рядок:

    //1. использовать двойные кавычки
    String x = "abc";

    //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

    Для того щоб докладніше дізнатися про те, як рядки зберігаються в пам'яті, читайте Create Java String Using "" or Constructor? .

Плани на майбутнє. Цей список заснований на моєму аналізі величезної кількості Open Source проектів з гітхабу, питань зі Stack Overflow та популярних запитів у Google. Не беруся довести, що вони дійсно входять до десятки найсерйозніших помилок, але вони насправді дуже поширені.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ