1. Порівняння об'єктів за допомогою оператора ==

Улюблена помилка програмістів-новачків — порівняння об'єктів (зокрема рядків) за допомогою оператора ==. Приклад:

Scanner console = new Scanner(System.in);
String s1 = console.nextLine();
String s2 = console.nextLine();
if (s1 == s2)
{
   System.out.println("Рядки однакові");
}

Такий код ніколи не виведе напис «Рядки однакові», тому що в операторі if відбувається порівняння посилань на два різні рядкові об'єкти.

Правильний варіант коду такий:

Scanner console = new Scanner(System.in);
String s1 = console.nextLine();
String s2 = console.nextLine();
if (s1.equals(s2))
{
   System.out.println("Рядки однакові");
}


2. Змінення об'єкта String

Часто програмісти-новачки забувають, що всі об'єкти класу є незмінними (immutable) й усі методи класу String повертають новий об'єкт, а поточний об'єкт ніколи не змінюється.

Приклад:

String s = "Привіт";
s.toUpperCase(); // перетворення рядка у верхній регістр

Такий код дуже схожий на правильний, але він не працюватиме. Метод toUpperCase() не змінює об'єкт, для якого його викликано. Правильний код матиме такий вигляд:

String s = "Привіт";
String result = s.toUpperCase(); // перетворення рядка у верхній регістр


3. Не ініціалізовано об'єкти, які є елементами масиву

Ще одна поширена помилка програмістів-початківців — вони забувають ініціалізувати змінну-масив. Приклад:

int[] array;
array[0] = 1;
array[0] = 2;

Такий код не працюватиме: змінній array потрібно явно присвоїти посилання на об'єкт-контейнер, який буде зберігати елементи масиву.

int[] array = new int[10];
array[0] = 1;
array[0] = 2;


4. Підміна поля класу локальною змінною

Новачки не люблять придумувати довгі й осмислені імена для змінних. Вони часто дають змінним імена a, b, i тощо. Це може обернутися суттєвими помилками в коді, коли таких змінних декілька:

Записати число 99 у 100 комірок масиву
class Solution
{
  public static int a = 99;
  public static int i = 100;

  public static void main(String[] args)
  {
    int[] a = new int[i];
    for (int i = 0; i < 10; i++)
    {
      a[i] = a;
    }
  }
}

Наведений вище код не скомпілюється. Виправлений код матиме такий вигляд:

Записати число 99 у 100 комірок масиву
class Solution
{
   public static int value = 99;
   public static int count = 100;

   public static void main(String[] args)
   {
      int[] a = new int[count];
      for (int i = 0; i < count; i++)
      {
         a[i] = value;
      }
   }
}


5. Видалення елемента колекції

Досить часто трапляються ситуації, коли потрібно видалити з колекції певний елемент. Цей код може мати такий вигляд:

ArrayList<Integer> list = new ArrayList<Integer>();
Collections.addAll(list, 0, -5, -7, -12, 5, 15);

for (Integer value: list)
   if (value < 0)
      list.remove(value);

Такий код не працюватиме, тому що не можна проходити колекцію по елементах за допомогою циклу for-each і водночас змінювати цю саму колекцію.

Є кілька варіантів розв'язання проблеми. По-перше, можна проходити одну колекцію, а змінювати іншу:

Варіант 1
ArrayList<Integer> list = new ArrayList<Integer>();
Collections.addAll(list, 0, -5, -7, -12, 5, 15);

ArrayList<Integer> copy = new ArrayList<Integer>(list);
for (Integer value: copy)
   if (value < 0)
      list.remove(value);

По-друге, починаючи з Java 8 у колекцій з'явився метод remove If(), в який можна передати правило (лямбда-функцію), що вказує, які елементи потрібно видалити.

Приклад:

Варіант 2
ArrayList<Integer> list = new ArrayList<Integer>();
Collections.addAll(list, 0, -5, -7, -12, 5, 15);

list.removeIf( x-> x<0 );


6. Розміщення в одному файлі декількох класів із модифікатором public

Кожен файл може містити тільки один публічний клас. Файл може містити оголошення й інших класів, але вони мають бути або внутрішніми класами публічного класу, або ж не повинні мати модифікатора public. Приклад:

Вміст файлу Solution.java Примітка
public class Solution
{
}
public class Main
{
}
Так не можна: два публічні класи в одному файлі.
public class Solution
{
}
class Main
{
}
А так можна. Клас Main — непублічний
public class Solution
{
  public static class Main
  {
  }
}
Так теж можна. Клас Main — вкладений клас


7. Виклик звичайних методів класу зі статичного методу main()

Часом програмісти-новачки звертаються до нестатичних змінних і методів із методу main() або інших статичних методів. Такий код, звісно, не працюватиме:

public class Solution
{
   public int n = 100;
   public int[] createArray()
   {
      return new int[n];
   }

   public static void main(String[]args)
   {
      int[] array = createArray();
   }
}

Метод main може звертатися тільки до статичного методу чи змінної. Можна також спочатку створити об'єкт класу Solution, а вже потім викликати нестатичні методи для цього об'єкта. Приклад:

Варіант 1 Варіант 2
public class Solution
{
  public static int n = 100;

  public static int[] createArray()
  {
    return new int[n];
  }

  public static void main(String[]args)
  {
    int[] array = createArray();
  }
}
public class Solution
{
  public int n = 100;

  public int[] createArray()
  {
    return new int[n];
  }

  public static void main(String[]args)
  {
    Solution sol = new Solution();
    int[] array = sol.createArray();
  }
}


8. Оголошення конструктора як методу

Ще одна поширена помилка — неправильне оголошення конструктора класу. Ім'я конструктора має збігатися з іменем класу, а типу результату конструктор не має. Приклади найчастіших помилок:

public class Person
{
   private String value;

   void Person(String value)
   {
      this.value = value;
   }
}




Тип результату тут не потрібен
public class Person
{
   private String value;

   constructor(String value)
   {
      this.value = value;
   }
}




Неправильне ім'я конструктора. Ім'я конструктора має збігатися з іменем класу
public class Person
{
   private String value;

   Person(String value)
   {
      value = value;
   }
}






Пропущено this — змінну value буде присвоєно самій собі
public class Person
{
   private String value;

   Person(String value)
   {
      this.value = value;
   }
}




Усе правильно


9. Неправильне успадкування інтерфейсів

Розробники Java намагалися зробити її якомога ближчою до англійської мови, тому для деяких споріднених понять вони вибрали різні ключові слова.

Коли клас успадковується від класу, потрібно використовувати ключове слово extends:

class Pet
{
}

class Cat extends Pet
{
}

Коли клас успадковується від інтерфейсу, потрібно використовувати ключове слово implements:

interface Meow
{
}

class Cat implements Meow
{
}

Коли інтерфейс успадковується від інтерфейсу, потрібно використовувати ключове слово extends:

interface Meow
{
}

interface Voice extends Meow
{
}


10. Пропуск слова break в операторі switch

І остання помилка в нашому переліку, але не остання для новачків — це пропуск оператора break в операторі множинного вибору switch. Приклад

Неправильно Правильно
LocalDate date = LocalDate.now();
DayOfWeek day = date.getDayOfWeek();
switch (day)
{
   case MONDAY:
      System.out.println("Понеділок");
   case TUESDAY:
      System.out.println("Вівторок");
   case WEDNESDAY:
      System.out.println("Середа");
   case THURSDAY:
      System.out.println("Четвер");
   case FRIDAY:
      System.out.println("П'ятниця");
   case SATURDAY:
      System.out.println("Субота");
   case SUNDAY:
      System.out.println("Неділя");
}
LocalDate date = LocalDate.now();
DayOfWeek day = date.getDayOfWeek();
switch (day)
{
   case MONDAY:
      System.out.println("Понеділок");
      break;
   case TUESDAY:
      System.out.println("Вівторок");
      break;
   case WEDNESDAY:
      System.out.println("Середа");
      break;
   case THURSDAY:
      System.out.println("Четвер");
      break;
   case FRIDAY:
      System.out.println("П'ятниця");
      break;
   case SATURDAY:
      System.out.println("Субота");
      break;
   case SUNDAY:
      System.out.println("Неділя");
      break;
}