Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

1.5. Управляющие конструкции

if/else, switch, for, while, do-while

Материалы

ТипСсылка
Документссылка
Видеоссылка

Способность выполнять код в зависимости от условий и повторять код пока условие истинно - это базовые строительные блоки большинства языков программирования. Давайте разберёмся, как управлять потоком выполнения в Java.

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

Ваш первый if

Оператор if позволяет запускать код только если условие истинно. Давайте создадим простой пример:

public class Main {
    public static void main(String[] args) {
        int number = 3;
        
        if (number < 5) {
            System.out.println("число меньше 5");
        }
    }
}

Если вы запустите этот код, вы увидите:

число меньше 5

Условие number < 5 проверяется, и если оно истинно, код внутри фигурных скобок выполняется. Если бы мы изменили number на 7, ничего не было бы выведено.

Обработка альтернативы с else

Часто нужно выполнить один блок кода если условие истинно, и другой блок если ложно. Для этого используется else:

int age = 16;

if (age >= 18) {
    System.out.println("Вы можете голосовать");
} else {
    System.out.println("Вы пока не можете голосовать");
}

Вывод:

Вы пока не можете голосовать

Поскольку age равен 16, условие age >= 18 ложно, поэтому выполняется блок else.

Множественные условия с else if

Что если нужно проверить несколько условий? Используйте else if:

int score = 75;

if (score >= 90) {
    System.out.println("Оценка: A");
} else if (score >= 80) {
    System.out.println("Оценка: B");
} else if (score >= 70) {
    System.out.println("Оценка: C");
} else if (score >= 60) {
    System.out.println("Оценка: D");
} else {
    System.out.println("Оценка: F");
}

Вывод:

Оценка: C

Java проверяет каждое условие по порядку и выполняет только первый подходящий блок. Даже если несколько условий истинны, выполнится только первое совпавшее.

Примечание: Использование слишком много else if может усложнить код. В таких случаях рассмотрите использование switch.

Оператор switch

Когда нужно сравнить одно значение с множеством вариантов, switch может быть более читаемым чем цепочка if-else.

Классический switch

Вот пример с днями недели:

int day = 3;

switch (day) {
    case 1:
        System.out.println("Понедельник");
        break;
    case 2:
        System.out.println("Вторник");
        break;
    case 3:
        System.out.println("Среда");
        break;
    default:
        System.out.println("Другой день");
}

Вывод:

Среда

Ключевое слово break важно! Без него выполнение “провалится” в следующий case.

Проваливание (Fall-through)

Иногда проваливание полезно для группировки случаев:

String month = "Январь";

switch (month) {
    case "Декабрь":
    case "Январь":
    case "Февраль":
        System.out.println("Зима");
        break;
    case "Март":
    case "Апрель":
    case "Май":
        System.out.println("Весна");
        break;
    default:
        System.out.println("Другой сезон");
}

Все три зимних месяца приведут к выводу “Зима”.

Современный switch (Java 14+)

Java 14 добавила более удобную форму switch - switch-выражения. Они возвращают значение и не требуют break:

int dayNum = 3;
String dayName = switch (dayNum) {
    case 1 -> "Понедельник";
    case 2 -> "Вторник";
    case 3 -> "Среда";
    case 4 -> "Четверг";
    case 5 -> "Пятница";
    case 6 -> "Суббота";
    case 7 -> "Воскресенье";
    default -> "Неверный день";
};

System.out.println(dayName);  // Выведет: Среда

Стрелка -> означает “вернуть это значение”. Никаких break не нужно!

Можно группировать случаи через запятую:

String dayType = switch (dayNum) {
    case 1, 2, 3, 4, 5 -> "Рабочий день";
    case 6, 7 -> "Выходной";
    default -> "Неверный день";
};

Если нужно выполнить несколько строк кода, используйте блок с yield:

int numLetters = switch (dayName) {
    case "Понедельник", "Воскресенье" -> 11;
    case "Среда" -> {
        System.out.println("Середина недели!");
        yield 5;  // yield возвращает значение
    }
    default -> 0;
};

Цикл while

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

int count = 0;

while (count < 5) {
    System.out.println("count: " + count);
    count++;
}

Вывод:

count: 0
count: 1
count: 2
count: 3
count: 4

Если условие сразу ложно, код внутри цикла не выполнится ни разу:

int x = 10;
while (x < 5) {
    System.out.println("Не выполнится");
}

Осторожно: Убедитесь что условие когда-нибудь станет ложным, иначе получится бесконечный цикл!

Цикл do-while

Цикл do-while похож на while, но проверяет условие после выполнения тела. Это означает, что код выполнится минимум один раз.

int number = 0;

do {
    System.out.println("number: " + number);
    number++;
} while (number < 3);

Вывод:

number: 0
number: 1
number: 2

Даже если условие изначально ложно, код выполнится один раз:

int x = 10;
do {
    System.out.println("Выполнится хотя бы раз");
} while (x < 5);  // условие ложно, но код уже выполнился

Вывод:

Выполнится хотя бы раз

Цикл for

Цикл for отлично подходит когда знаете сколько раз нужно повторить код.

Базовый for

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

Вывод:

i: 0
i: 1
i: 2
i: 3
i: 4

Цикл for состоит из трёх частей:

  1. Инициализация (int i = 0) - выполняется один раз в начале
  2. Условие (i < 5) - проверяется перед каждой итерацией
  3. Обновление (i++) - выполняется после каждой итерации

Все три части опциональны:

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

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

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

Вывод:

i=0, j=10
i=1, j=9
i=2, j=8
i=3, j=7
i=4, j=6

Enhanced for (for-each)

Для обхода массивов и коллекций есть упрощённая форма:

int[] numbers = {10, 20, 30, 40, 50};

for (int num : numbers) {
    System.out.println(num);
}

Вывод:

10
20
30
40
50

Читается как “для каждого num в numbers”. Это намного проще чем обычный for с индексами!

Работает с любыми коллекциями:

List<String> names = Arrays.asList("Анна", "Боб", "Карл");

for (String name : names) {
    System.out.println("Привет, " + name);
}

Ограничение: Enhanced for не позволяет изменять элементы массива или получать индекс. Для этого используйте обычный for.

Прерывание потока: break и continue

break - выход из цикла

break немедленно завершает цикл:

for (int i = 0; i < 10; i++) {
    if (i == 5) {
        break;  // выход из цикла
    }
    System.out.println(i);
}

Вывод:

0
1
2
3
4

Полезно для поиска элемента:

int[] numbers = {5, 10, 15, 20, 25};
int target = 15;
boolean found = false;

for (int num : numbers) {
    if (num == target) {
        found = true;
        break;  // нашли, выходим
    }
}

System.out.println("Найдено: " + found);

continue - пропуск итерации

continue пропускает оставшийся код в текущей итерации и переходит к следующей:

for (int i = 0; i < 5; i++) {
    if (i == 2) {
        continue;  // пропускаем i=2
    }
    System.out.println(i);
}

Вывод:

0
1
3
4

Полезно для фильтрации:

int[] numbers = {-5, 10, -3, 15, -7, 20};
int sum = 0;

for (int num : numbers) {
    if (num < 0) {
        continue;  // пропускаем отрицательные
    }
    sum += num;
}

System.out.println("Сумма положительных: " + sum);  // 45

Метки для вложенных циклов

Когда нужно выйти из вложенного цикла, используйте метки:

outer: for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        if (i == 1 && j == 1) {
            break outer;  // выход из ОБОИХ циклов
        }
        System.out.println("i=" + i + ", j=" + j);
    }
}

Вывод:

i=0, j=0
i=0, j=1
i=0, j=2
i=1, j=0

Это работает и с continue:

outer: for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        if (j == 1) {
            continue outer;  // к следующей итерации внешнего цикла
        }
        System.out.println("i=" + i + ", j=" + j);
    }
}

Оператор return

return завершает метод и возвращает управление:

public static int findMax(int a, int b) {
    if (a > b) {
        return a;
    }
    return b;
}

Можно использовать несколько return:

public static String getGrade(int score) {
    if (score >= 90) return "A";
    if (score >= 80) return "B";
    if (score >= 70) return "C";
    if (score >= 60) return "D";
    return "F";
}

Для void методов return просто выходит:

public static void printPositive(int num) {
    if (num <= 0) {
        return;  // выход из метода
    }
    System.out.println("Положительное: " + num);
}

Обработка исключений

try-catch

Обрабатывайте ошибки чтобы программа не упала:

try {
    int result = 10 / 0;  // ошибка деления на ноль
} catch (ArithmeticException e) {
    System.out.println("Ошибка: нельзя делить на ноль!");
}

Можно ловить несколько типов исключений:

try {
    String text = null;
    System.out.println(text.length());
} catch (NullPointerException e) {
    System.out.println("Переменная null");
} catch (Exception e) {
    System.out.println("Другая ошибка");
}

finally - код который выполнится всегда

Блок finally выполняется независимо от того, было ли исключение:

FileReader file = null;
try {
    file = new FileReader("data.txt");
    // читаем файл
} catch (IOException e) {
    System.out.println("Ошибка чтения");
} finally {
    if (file != null) {
        try {
            file.close();  // закрываем файл в любом случае
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

try-with-resources

Java 7+ позволяет автоматически закрывать ресурсы:

try (FileReader file = new FileReader("data.txt")) {
    // читаем файл
} catch (IOException e) {
    System.out.println("Ошибка чтения");
}
// файл закроется автоматически!

Это работает с любыми AutoCloseable ресурсами.

Оператор assert

Проверяйте условия во время разработки:

int age = 15;
assert age >= 18 : "Возраст должен быть >= 18";

Важно: Assertions по умолчанию отключены! Включите их флагом -ea при запуске:

java -ea Main

С сообщением:

double price = -10.0;
assert price > 0 : "Цена должна быть положительной, а не " + price;

Примечание: Не используйте assertions для проверки аргументов в публичных методах. Используйте исключения вместо этого.

Pattern Matching (Java 16+)

instanceof с паттернами

Старый способ:

Object obj = "Hello";

if (obj instanceof String) {
    String s = (String) obj;  // нужно приведение типа
    System.out.println(s.toUpperCase());
}

Новый способ (Java 16+):

Object obj = "Hello";

if (obj instanceof String s) {  // s уже String!
    System.out.println(s.toUpperCase());
}

Switch с паттернами (Java 17+)

Object obj = 42;

String result = switch (obj) {
    case Integer i -> "Число: " + i;
    case String s -> "Строка: " + s;
    case null -> "Это null";
    default -> "Неизвестный тип";
};

С условиями (guards):

Object obj = 42;

String description = switch (obj) {
    case Integer i when i > 0 -> "Положительное число";
    case Integer i when i < 0 -> "Отрицательное число";
    case Integer i -> "Ноль";
    default -> "Не число";
};

Record Patterns (Java 21+)

record Point(int x, int y) {}

Point p = new Point(3, 4);

String location = switch (p) {
    case Point(0, 0) -> "Начало координат";
    case Point(int x, 0) -> "На оси X: " + x;
    case Point(0, int y) -> "На оси Y: " + y;
    case Point(int x, int y) -> "Точка (" + x + ", " + y + ")";
};

Полезные советы

Используйте фигурные скобки

Даже для однострочных блоков:

// Плохо
if (condition)
    doSomething();

// Хорошо
if (condition) {
    doSomething();
}

Это предотвращает ошибки при добавлении кода.

Извлекайте сложные условия

// Плохо
if (user.getAge() >= 18 && user.hasLicense() && !user.isBanned()) {
    allowDriving();
}

// Хорошо
boolean canDrive = user.getAge() >= 18 
                && user.hasLicense() 
                && !user.isBanned();
if (canDrive) {
    allowDriving();
}

Избегайте магических чисел

// Плохо
if (status == 1) {
    // что означает 1?
}

// Хорошо
final int STATUS_ACTIVE = 1;
if (status == STATUS_ACTIVE) {
    // ясно!
}

// Ещё лучше - enum
enum Status { ACTIVE, INACTIVE, PENDING }
if (status == Status.ACTIVE) {
    // идеально!
}

Предпочитайте ранний выход

// Плохо
public void process(String data) {
    if (data != null) {
        if (data.length() > 0) {
            // много кода
        }
    }
}

// Хорошо
public void process(String data) {
    if (data == null || data.length() == 0) {
        return;  // ранний выход
    }
    // много кода
}

Итоги

Вы изучили основные управляющие конструкции Java:

  • if, else if, else для условного выполнения
  • switch для множественного выбора (классический и современный)
  • while и do-while для циклов с условием
  • for и enhanced for для итерации
  • break, continue, return для управления потоком
  • try-catch-finally для обработки ошибок
  • Pattern matching для современного Java

Теперь вы можете эффективно управлять потоком выполнения ваших программ!

Практика

Попробуйте написать программы для:

  1. Проверки является ли год високосным
  2. Вычисления факториала числа
  3. Поиска всех простых чисел до N
  4. Конвертации температуры между Цельсием и Фаренгейтом

Удачи в программировании!