Перейти к основному содержимому

Операторы и циклы в Java

Разработчику Архитектору

Перед чтением: Операторы — общие понятия оператора, операнда, приоритетов; побитовые операторы — что такое побитовая операция и чем она отличается от логической.

Сначала: Циклы в коде — общая идея повторений, виды циклов и типичные ошибки без привязки к синтаксису языка.


Операторы и циклы в Java

Виды операторов

Операторы можно разделить на несколько видов:

Если вы только начинаете, полезно воспринимать операторы как "глаголы" языка:

  • арифметические меняют числа;
  • логические принимают решения;
  • присваивания изменяют состояние переменной;
  • условные выбирают ветку выполнения. Так проще перейти от заучивания таблицы к пониманию, как эти конструкции работают вместе в реальном коде.
ТипПримеры
Арифметические+ — сложение, - — вычитание, * — умножение, / — деление, % — остаток от деления
Сравнения== — равно, != — не равно, > — больше, < — меньше, >= — больше или равно, <= — меньше или равно
Логические! — отрицание (NOT), & — конъюнкция (AND), &#124; — дизъюнкция (OR), ^ — исключающее ИЛИ (XOR), && — сокращённая конъюнкция, &#124;&#124; — сокращённая дизъюнкция, ~ — побитовое дополнение (NOT)
Присваивания=, +=, -=, *=, /=
Тернарныйусловие ? значение_если_истина : значение_если_ложь
Условные операторыif-else, switch
Прочие++ — инкремент (увеличение на 1), -- — декремент (уменьшение на 1), // — однострочный комментарий, /* */ — многострочный комментарий

Play ITЗагрузка интерактивного демо…


Сравнение

Для примитивных типов (например, int, double) оператор == сравнивает значения.

Для объектов (например, строк или пользовательских классов) оператор == сравнивает ссылки на объекты, то есть их физическое местоположение в памяти.

Чтобы сравнить содержимое объектов, используется метод .equals().

На практике правило простое — всё, что относится к бизнес-данным (String, UUID, сущности, DTO), сравнивайте через equals() или специализированные методы (compareTo, Comparator). Оператор == оставляйте для примитивов и редких случаев проверки идентичности ссылки.

В Java есть методы, которые выполняют функции, аналогичные операторам:

  • .equals() — аналог оператора == для сравнения содержимого объектов.
  • .compareTo() — используется для сравнения строк или объектов, реализующих интерфейс Comparable;
  • Math.addExact() , Math.subtractExact() , Math.multiplyExact() — аналоги операторов +, -, *, но с проверкой на переполнение. Если результат выходит за пределы допустимых значений, выбрасывается исключение ArithmeticException;
  • Integer.bitCount() — подсчитывает количество единиц в двоичном представлении числа. Аналогично работе с битовыми операциями (&, |, ^);
  • Objects.equals() — безопасный способ сравнения двух объектов, который обрабатывает null. Если оба объекта равны null, метод вернёт true;
  • Integer.parseInt() , Double.parseDouble() — аналоги операторов приведения типов (например, (int)).

Метод equals() проверяет логическое равенство содержимого объектов. Для строк сравнение учитывает последовательность символов.

Код ITЗагрузка примера кода…

Разбор:

  • new String("привет") в text1 и text2 создает разные объекты, поэтому == возвращает false.
  • equals(...) у String сравнивает содержимое, поэтому для одинаковых символов результат true.
  • Для Integer с небольшими значениями срабатывает кэш оберток, поэтому num1 == num2 может быть true.
  • Для более крупных значений (200) создаются разные объекты, и == снова дает false.
  • Практическое правило: для объектов сравнивайте значение через equals, а == оставляйте для примитивов и проверки идентичности ссылки.

При создании собственных классов метод equals() следует переопределять вместе с hashCode():

Код ITЗагрузка примера кода…

Разбор:

  • @Override equals(...) определяет критерий логического равенства по полям name и age.
  • Проверка this == obj быстро обрабатывает случай сравнения объекта с самим собой.
  • Условие с getClass() защищает от сравнения объектов разных типов.
  • hashCode() вычисляется из тех же полей, что и equals, чтобы соблюсти контракт коллекций HashMap и HashSet.
  • Без согласованной пары equals/hashCode равные объекты могут вести себя некорректно в хеш-структурах.

Метод compareTo() возвращает целое число:

  • отрицательное;
  • если текущий объект меньше аргумента;
  • ноль при равенстве;
  • положительное при превосходстве.

Код ITЗагрузка примера кода…

Разбор:

  • compareTo(...) возвращает знак отношения: отрицательное, ноль или положительное значение.
  • Для строк сравнение лексикографическое и зависит от Unicode-порядка символов.
  • Для Integer метод сравнивает числовые значения без дополнительного преобразования.
  • fruits.sort(String::compareTo) использует метод compareTo как компаратор для сортировки списка.
  • Такой единый контракт сравнения лежит в основе сортировок и структур вроде TreeSet.

Методы Math.addExact(), Math.subtractExact(), Math.multiplyExact() контролируют переполнение целочисленных типов.

Код ITЗагрузка примера кода…

Разбор:

  • max + 1 демонстрирует тихое переполнение int, когда значение циклически переходит в отрицательный диапазон.
  • Math.addExact(...) и Math.multiplyExact(...) проверяют переполнение и выбрасывают ArithmeticException.
  • try-catch позволяет явно отработать аварийный сценарий, а не продолжить с поврежденным результатом.
  • Math.subtractExact(...) делает ту же защиту для вычитания и полезен в финансовой и учетной логике.
  • Такой подход повышает надежность расчетов, где потеря точности или знак результата критичны.

Метод Integer.bitCount() возвращает количество установленных битов (единиц) в двоичном представлении числа.

Код ITЗагрузка примера кода…

Метод Objects.equals() корректно обрабатывает значения null, избегая NullPointerException.

String s1 = null;
String s2 = null;
String s3 = "текст";

System.out.println(Objects.equals(s1, s2)); // true — оба null
System.out.println(Objects.equals(s1, s3)); // false — null и непустая строка
System.out.println(Objects.equals(s3, s3)); // true — одинаковые строки

// Сравнение без Objects.equals() требует дополнительных проверок
if (s1 != null && s1.equals(s2)) {
// безопасное сравнение без использования Objects.equals()
}

Методы Integer.parseInt() и Double.parseDouble() преобразуют текстовые представления в числовые значения.

Код ITЗагрузка примера кода…


Класс Object

Класс Object — базовый для всех классов в Java. Он определяет поведение, доступное каждому объекту.

Основные методы:

  • equals(Object obj) — сравнение объектов на логическое равенство
  • hashCode() — возвращает хеш-код объекта
  • toString() — строковое представление объекта
  • getClass() — возвращает объект Class, представляющий тип в рантайме
  • clone() — создает копию объекта (если поддерживает Cloneable)
  • finalize() — вызывается перед сборкой мусора (устарел и не рекомендуется)
  • wait(), notify(), notifyAll() — методы для межпоточной синхронизации.

Метод equals() определяет логическое равенство объектов. Метод hashCode() генерирует целочисленный хеш-код, используемый в хеш-коллекциях.

Код ITЗагрузка примера кода…

Метод toString() предоставляет текстовое представление объекта. Переопределение улучшает отладку и логирование.

Код ITЗагрузка примера кода…

Метод getClass() возвращает объект класса Class, описывающий тип объекта во время выполнения.

String text = "пример";
Class<?> clazz = text.getClass();
System.out.println(clazz.getName()); // java.lang.String
System.out.println(clazz.getSimpleName()); // String

Integer number = 42;
System.out.println(number.getClass().getName()); // java.lang.Integer

// Проверка типа во время выполнения
Object obj = "текст";
if (obj.getClass() == String.class) {
System.out.println("Это строка");
}

Метод clone() создаёт поверхностную копию объекта. Класс должен реализовывать интерфейс Cloneable.

Код ITЗагрузка примера кода…

Методы синхронизации — wait(), notify(), notifyAll() координируют работу потоков при доступе к общим ресурсам.

Код ITЗагрузка примера кода…

Метод finalize() объявлен устаревшим в Java 9 и удалён в последующих версиях. Для освобождения ресурсов следует использовать интерфейс AutoCloseable и конструкцию try-with-resources.


Условные операторы

Условия (условные операторы) управляют потоком выполнения программы, позволяют выполнять разный код, в зависимости от выполнения определённого условия.


if-else

Условный оператор if-else (если/иначе) выбирает путь выполнения программы на основе логического выражения.

int temperature = 22;

if (temperature > 30) {
System.out.println("Жарко");
} else if (temperature > 20) {
System.out.println("Тепло"); // выполняется эта ветка
} else if (temperature > 10) {
System.out.println("Прохладно");
} else {
System.out.println("Холодно");
}

Синтаксис:

if (условие) {
// выполняется, если условие истинно
} else {
// выполняется, если условие ложно
}

Пример:

int age = 18;
if (age >= 18) {
System.out.println("Вы совершеннолетний");
} else {
System.out.println("Вы несовершеннолетний");
}

Особенности синтаксиса:

  • Фигурные скобки можно опустить для одиночного оператора, но такой стиль не рекомендуется
  • Условие всегда приводится к булевому типу
  • Ветки else if проверяются последовательно сверху вниз
  • Выполняется только первая удовлетворяющая условию ветка

Примеры с логическими операторами:

Код ITЗагрузка примера кода…

Вложенные условия:

int hour = 14;
boolean isHoliday = false;

if (!isHoliday) {
if (hour >= 9 && hour < 18) {
System.out.println("Рабочее время");
} else {
System.out.println("Нерабочее время");
}
} else {
System.out.println("Праздничный день");
}

switch

Оператор switch выбирает одну из нескольких веток выполнения на основе значения выражения.

Синтаксис:

switch (выражение) {
case значение1:
// код
break;
case значение2:
// код
break;
default:
// код по умолчанию
}

Пример:

int day = 3;
switch (day) {
case 1 -> System.out.println("Понедельник");
case 2 -> System.out.println("Вторник");
case 3 -> System.out.println("Среда");
default -> System.out.println("Неизвестный день");
}

Традиционный синтаксис с оператором break:

Код ITЗагрузка примера кода…

Современный синтаксис с оператором -> (доступен с Java 14):

String day = "вторник";

switch (day) {
case "понедельник" -> System.out.println("Начало недели");
case "вторник", "среда", "четверг" -> System.out.println("Середина недели"); // выполняется эта ветка
case "пятница" -> System.out.println("Конец рабочей недели");
case "суббота", "воскресенье" -> System.out.println("Выходные");
default -> System.out.println("Неизвестный день");
}

Оператор switch как выражение (возвращает значение):

int statusCode = 404;
String statusMessage = switch (statusCode) {
case 200 -> "OK";
case 404 -> "Not Found"; // возвращается это значение
case 500 -> "Internal Server Error";
default -> "Unknown Status";
};
System.out.println(statusMessage); // Not Found

Правила использования:

  • Выражение в switch может иметь тип byte, short, char, int, enum, String или их обёртки
  • Каждая метка case должна содержать константное выражение
  • Оператор break предотвращает переход к следующей ветке (проваливание)
  • Ветка default выполняется, если ни одна метка case не совпала
  • В синтаксисе с -> оператор break не требуется — выполняется только одна ветка

Циклы

Циклы позволяют выполнять блок кода несколько раз.

КонструкцияПорядокКогда удобна
forинициализация → проверка → тело → обновлениеизвестно число повторений или нужен индекс
for (тип x : коллекция)следующий элемент → телобезопасный перебор массива, List, Set
whileпроверка условия → телочисло итераций заранее неизвестно
do…whileтело → проверка условияминимум один проход (меню, повтор ввода)

Аналог for…of в JavaScript — цикл for-each (for (int value : values)). Обобщённо — циклы в коде, по JavaScript для сравнения синтаксиса — циклы в JavaScript. Готовые задачи с разбором (FizzBuzz, Fibonacci, пузырёк, линейный поиск) — Lab — консольные задачи на Java.


Интерактивное демо — пошаговый цикл на примере JavaScript (for, while). В Java синтаксис другой, но порядок шагов тот же. Обобщённо: циклы в коде.

Play ITЗагрузка интерактивного демо…


for

for (для) используется, когда известно количество повторений.

Синтаксис:

for (инициализация; условие; обновление) {
// тело цикла
}

Пример:

for (int i = 0; i < 5; i++) {
System.out.println("i = " + i);
}

Структура содержит три компонента:

  • инициализацию;
  • условие;
  • обновление.
for (int i = 0; i < 5; i++) {
System.out.println("Итерация " + i);
}
// Вывод: Итерация 0, Итерация 1, Итерация 2, Итерация 3, Итерация 4

Инициализация выполняется один раз перед началом цикла. Обычно здесь объявляется и присваивается начальное значение счётчику.

for (int counter = 10; counter > 0; counter--) {
System.out.println(counter);
}
// Обратный отсчёт от 10 до 1

Можно объявить несколько переменных одного типа:

for (int i = 0, j = 10; i < 5; i++, j--) {
System.out.println("i=" + i + ", j=" + j);
}

Условие проверяется перед каждой итерацией. Цикл продолжается, пока условие истинно.

int limit = 3;
for (int i = 0; i < limit; i++) {
System.out.println(i);
}
// Вывод: 0, 1, 2

Бесконечный цикл создаётся опущением условия или указанием константы true:

for (;;) {
// бесконечный цикл
break; // выход по условию внутри тела
}

Обновление выполняется после каждой итерации. Обычно здесь изменяется значение счётчика.

for (int i = 1; i <= 100; i *= 2) {
System.out.println(i);
}
// Вывод: 1, 2, 4, 8, 16, 32, 64

Инкремент i++ увеличивает значение на единицу. Декремент i-- уменьшает на единицу. Можно использовать составные операторы: i += 5, i *= 2.

Каждое выполнение тела цикла называется итерацией. Шаг — изменение счётчика за одну итерацию.

// Шаг равен 3
for (int i = 0; i < 15; i += 3) {
System.out.println(i);
}
// Вывод: 0, 3, 6, 9, 12

// Отрицательный шаг
for (int i = 10; i >= 0; i -= 2) {
System.out.println(i);
}
// Вывод: 10, 8, 6, 4, 2, 0

Цикл for подходит для перебора массивов и коллекций:

int[] numbers = {5, 10, 15, 20};
for (int i = 0; i < numbers.length; i++) {
System.out.println("Элемент " + i + ": " + numbers[i]);
}

String[] words = {"яблоко", "банан", "апельсин"};
for (int i = 0; i < words.length; i++) {
System.out.println((i + 1) + ". " + words[i]);
}

Улучшенный цикл for (for-each) упрощает перебор элементов:

int[] values = {1, 2, 3, 4, 5};
for (int value : values) {
System.out.println(value * value);
}
// Вывод: 1, 4, 9, 16, 25

while

Цикл while повторяет выполнение блока кода, пока логическое условие остаётся истинным. Проверка условия происходит перед каждой итерацией.

Синтаксис:

while (условие) {
// тело цикла
}

Пример:

int i = 0;
while (i < 5) {
System.out.println("i = " + i);
i++;
}
int count = 3;
while (count > 0) {
System.out.println("Осталось: " + count);
count--;
}
// Вывод: Осталось: 3, Осталось: 2, Осталось: 1

Особенности:

  • Если условие изначально ложно, тело цикла не выполнится ни разу
  • Счётчик или переменная условия должны изменяться внутри тела цикла
  • Бесконечный цикл возникает при отсутствии изменения условия

Примеры использования:

Код ITЗагрузка примера кода…

Цикл while удобен, когда количество итераций заранее неизвестно и определяется в процессе выполнения.


do-while

do-while (делать, пока), выполняется хотя бы один раз, даже если условие ложно. Проверка условия происходит после каждой итерации.

Синтаксис:

do {
// тело цикла
} while (условие);

Пример:

int j = 0;
do {
System.out.println("j = " + j);
j++;
} while (j < 5);
int attempts = 0;
do {
System.out.println("Попытка " + (attempts + 1));
attempts++;
} while (attempts < 3);
// Вывод: Попытка 1, Попытка 2, Попытка 3

Сравнение с while:

Код ITЗагрузка примера кода…

Практическое применение — интерактивные меню:

Код ITЗагрузка примера кода…

Цикл do-while применяется, когда требуется как минимум одна итерация независимо от начального состояния условия.


Как выбирать конструкцию на практике

ЗадачаПодход
Фиксированное число повторенийfor
Повтор до выполнения условияwhile
Нужна хотя бы одна итерацияdo-while
Много веток по одному значениюswitch
Простой бинарный выборif-else
Хорошая привычка

Сначала пишите вариант, который проще всего читать через месяц.

Для большинства задач это if-else и обычный for, а не "умные" сокращения.

Закрепить циклы на типовых задачах — Lab — консольные задачи.

Следующий шаг с GUI — Lab — Swing (конвертер, список задач).


Типичные ошибки в операторах и циклах

  • Сравнение строк через == вместо equals().
  • Пропущенный break в классическом switch.
  • Условие цикла с <= вместо <, из-за чего возникает выход за границы массива.
  • Бесконечный цикл из-за отсутствия изменения счётчика.
  • Использование & вместо && и | вместо || без понимания разницы.

Связанные статьи