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

Операторы и циклы в 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.
  • Условие цикла с <= вместо <, из-за чего возникает выход за границы массива.
  • Бесконечный цикл из-за отсутствия изменения счётчика.
  • Использование & вместо && и | вместо || без понимания разницы.

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