Когда у вас нет счётчика
Цикл for построен вокруг счётчика, который вы задаёте заранее. Но у многих циклов нет понятного счёта: читать строки, пока не закончится файл, запрашивать пароль, пока он не окажется верным, делить число пополам, пока оно не дойдёт до 1. Для таких случаев цикл while подходит естественно — он просто повторяется, пока условие остаётся истинным.
Цикл while проверяет условие перед каждым проходом, включая самый первый. Если условие ложно с самого начала, тело не выполняется вовсе.
Условие count > 0 проверяется первым; если оно истинно, выполняется тело; затем мы возвращаемся в начало и проверяем снова. Строка count-- — это то, что в итоге делает условие ложным; уберите её, и цикл будет выполняться вечно.
Анатомия цикла while
Сравните его с трёхчастным циклом for, который вы уже знаете. Цикл while разносит эти три задачи: инициализацию вы делаете до цикла, условие идёт в скобках, а обновление живёт внутри тела.
int i = 0; // инициализация - до цикла
while (i < 5) { // условие - проверяется на каждом проходе
System.out.println(i);
i++; // обновление - о нём нужно помнить самому
}
Эта последняя часть и есть подвох. В цикле for обновление стоит прямо рядом с условием, поэтому его трудно забыть. В цикле while это просто ещё один оператор в теле — самая частая ошибка как раз в том, чтобы пропустить его и подвесить программу.
do-while: выполнить тело хотя бы один раз
Иногда нужно, чтобы тело выполнилось один раз прежде, чем вы вообще сможете решить, повторять ли. Цикл do-while проверяет условие в конце, поэтому тело всегда выполняется хотя бы один раз:
Нужно запросить и прочитать ввод хотя бы один раз, прежде чем вы сможете понять, корректен ли он, — именно это и даёт do-while. Обратите внимание на точку с запятой после while (...) — она обязательна, и её отсутствие — частая ошибка компиляции.
Отличие от обычного while ясно видно, когда условие изначально ложно:
Печатается только do-while body. Цикл while полностью пропускает своё тело, потому что x < 5 было ложным до первой проверки.
Цикл до сторожевого значения
Классическое применение while — читать до специального «стоп»-значения, сторожевого (sentinel). Здесь нет счётчика; вы просто продолжаете, пока данные не скажут вам остановиться:
Шаблон такой: прочитать один раз до цикла, затем читать снова в конце каждого прохода. Так условие всегда проверяет свежий ввод. Если вы забудете второе чтение внутри тела, value никогда не изменится — и вы получите бесконечный цикл.
break и continue в циклах while
break и continue работают здесь так же, как в цикле for. break немедленно покидает цикл; continue сразу переходит к проверке условия, пропуская остаток текущего прохода:
Это печатает 1 3 5 7 9. while (true) намеренно никогда не останавливается сам по себе — единственный выход — break. Будьте осторожны с continue в цикле while: поскольку обновление является частью тела, переход в начало через continue до того, как вы продвинули счётчик, — это тихий способ подвесить цикл. В примере выше n++ выполняется первым, поэтому всё безопасно.
Остерегайтесь бесконечных циклов
Условие должно в итоге стать ложным, и это целиком зависит от того, что что-то внутри тела меняется. Два обычных виновника — забыть про обновление и обновлять неправильно:
int i = 0;
while (i < 5) {
System.out.println(i); // i никогда не меняется -> выполняется вечно
}
int i = 0;
while (i != 10) {
i += 3; // 0, 3, 6, 9, 12... проскакивает мимо 10 -> выполняется вечно
}
Первый зависает, потому что i никогда не обновляется. Второй зависает, потому что счётчик перешагивает через точное значение, которое ищет условие; предпочитайте < или <= вместо !=, когда шаг может не попасть точно. while (true) допустим, если у него есть гарантированный break; случайный — это просто баг.
Далее: цикл for-each
Цикл while — правильный инструмент, когда вы повторяете до изменения условия и нет понятного счёта. Но когда вы просто хотите пройти по каждому элементу массива или списка — без индекса, без условия для управления — есть кое-что ещё чище: усовершенствованный цикл for-each. Это следующая страница.
Часто задаваемые вопросы
В чём разница между while и do-while в Java?
Цикл while проверяет условие до первого прохода, поэтому тело может выполниться ноль раз. Цикл do-while сначала один раз выполняет тело, а затем проверяет условие в конце — поэтому он всегда выполняется хотя бы один раз. Используйте do-while, когда работу нужно сделать прежде, чем вы сможете решить, повторять ли её (например, запросить ввод, а затем проверить его).
Когда использовать цикл while вместо for в Java?
Используйте цикл while, когда у вас нет понятного счётчика и вы просто хотите повторять пока некое условие не изменится — читать ввод, пока пользователь не наберёт quit, опрашивать, пока значение не будет готово, или обрабатывать очередь, пока она не опустеет. Берите цикл for, когда знаете число итераций или у вас есть очевидный индекс для счёта.
Как остановить бесконечный цикл while в Java?
Убедитесь, что что-то внутри тела в итоге делает условие ложным (уменьшает счётчик, продвигает указатель, выставляет флаг). Для намеренного цикла while (true) поместите внутрь break, защищённый if. Если цикл зависает, обычная причина — забыли обновить переменную, от которой зависит условие.