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 и т.д. Это может сыграть злую шутку, когда таких переменных в коде несколько:

Занести в 100 ячеек массива число 99
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;
    }
  }
}

Код выше не скомпилируется. Исправленный вариант будет выглядеть так:

Занести в 100 ячеек массива число 99
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, появился метод removeIf(), в который можно передать правило (лямбда-функцию), указывающее, какие элементы нужно удалить.

Пример:

Решение 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;
}