while·break·continue
반복 상태가 종료 방향으로 움직이는지 추적하고 break와 continue로 회원가입 배치 입력을 제어합니다.
반복 횟수가 아니라 “아직 처리할 입력이 있는가”, “종료 명령을 만났는가”가 계속 여부를 정하면 while이 자연스럽습니다.
반복문은 조건만으로 끝나지 않습니다.
본문이 조건에 사용한 상태를 언젠가 거짓이 되는 방향으로 바꾸는지 함께 확인해야 합니다.
무한 반복 재현
끝없는 프로그램을 그대로 두면 실습 터미널을 멈춰야 하므로 다음 코드는 네 번째 회차에 예외를 던지는 감시 장치를 넣었습니다.
핵심 결함인 count 미변경은 그대로입니다.
public final class NonProgressingWhile {
public static void main(String[] args) {
int count = 0;
int watchdog = 0;
while (count < 3) {
System.out.println("count=" + count);
watchdog++;
if (watchdog == 4) {
throw new IllegalStateException("count did not progress");
}
}
}
}count=0
count=0
count=0
count=0
Exception in thread "main" java.lang.IllegalStateException: count did not progress조건 count < 3을 다시 검사할 때마다 count는 여전히 0입니다.
watchdog는 오류를 재현하기 위한 안전장치일 뿐 해결책이 아닙니다.
정상 반복은 본래 상태인 count를 증가시킵니다.
public final class WhileProgress {
public static void main(String[] args) {
int count = 0;
while (count < 3) {
System.out.println("count=" + count);
count++;
}
System.out.println("done=" + count);
}
}count=0
count=1
count=2
done=3초기 상태 0, 계속 조건 < 3, 본문의 변화 count++를 한 세트로 읽습니다.
하나가 빠지면 시작하지 않거나, 너무 많이 실행하거나, 끝나지 않습니다.
do-while의 최초 실행
while은 본문 전에 조건을 검사합니다.
시작부터 거짓이면 0회 실행합니다.
do-while은 본문 뒤에서 조건을 검사해 최소 한 번 실행합니다.
public final class PreAndPostCheck {
public static void main(String[] args) {
int remaining = 0;
while (remaining > 0) {
System.out.println("while=" + remaining);
}
do {
System.out.println("do-while=" + remaining);
} while (remaining > 0);
}
}do-while=0메뉴를 최소 한 번 보여 준 뒤 계속 여부를 묻는 구조에는 do-while이 맞을 수 있습니다.
반대로 입력이 없으면 아무것도 처리하지 않아야 하는 배치 작업에 쓰면 가짜 한 회가 생깁니다.
“최소 한 번 실행”이 실제 요구인지 먼저 확인합니다.
FLOWCHART · LOOP PROGRESS
while 종료성은 검사 위치와 상태 진전이 결정한다
while은 조건에서 시작하고 do-while은 본문에서 시작하지만, 두 반복 모두 본문이 상태를 종료 방향으로 움직인 뒤 조건으로 돌아와야 끝납니다.
반복 종료 계약 첫 검사 위치와 매 회 상태 진전을 분리해 봅니다.
while은 조건부터
while은 본문 전에 조건을 검사하므로 처음부터 false면 본문을 0회 실행합니다.do-while은 본문부터
do-while은 본문 뒤에서 조건을 검사하므로 처음 조건이 false여도 본문을 최소 1회 실행합니다.종료 가능한 전체 계약
초기 상태, 조건, 본문, 상태 진전, 조건 복귀가 한 세트입니다. 본문은 조건에 쓰인 상태를 종료 방향으로 움직여야 합니다.
성공 trace
count++가 매 회 실행되면0→1→2→3→done으로 진전되어count < 3을 빠져나갑니다.진전 누락 trace
count++가 없으면0→0→…으로 같은 상태를 반복하며 watchdog 같은 외부 안전장치 없이는 종료되지 않습니다.
- 조건·본문·조건 복귀
- 상태 진전이 누락된 미종료 trace
- 종료 방향의 상태 진전
while과 do-while의 차이는 첫 검사 위치이고, 두 반복의 공통 종료 조건은 모든 반복 경로에서 상태가 실제로 진전되는 것입니다.
break와 continue
public final class LoopControl {
public static void main(String[] args) {
int value = 0;
while (value < 8) {
value++;
if (value % 2 == 0) {
continue;
}
if (value > 5) {
break;
}
System.out.println(value);
}
System.out.println("stoppedAt=" + value);
}
}1
3
5
stoppedAt=7짝수에서는 continue가 아래 문장을 건너뛰고 조건 검사로 돌아갑니다.
7에서는 break가 while 자체를 끝냅니다.
value++를 continue 아래에 두면 짝수에서 증가가 건너뛰어 같은 값에 갇힐 수 있습니다.
반복 상태 갱신은 continue보다 먼저 실행되거나 모든 경로에서 실행됨이 보장돼야 합니다.
break와 continue가 중첩 반복 안에 있으면 기본적으로 가장 가까운 반복문 하나에만 적용됩니다.
바깥 반복까지 제어할 필요가 있다면 메서드로 추출하거나 라벨을 쓸 수 있지만 먼저 구조를 단순화할 방법을 찾습니다.
while 기반 일괄 입력
batch 뒤에는 email:age 토큰이 여러 개 오며 quit을 만나면 남은 입력을 처리하지 않습니다.
이 절에서는 나이 부분이 정수라는 입력 전제를 두고 반복 제어에 집중합니다.
숫자가 아닌 문자열까지 복구하는 try/catch는 ch3-2에서 처음 정의합니다.
public final class BatchWhile {
public static void main(String[] args) {
int index = 0;
int savedCount = 0;
int totalAge = 0;
while (index < args.length) {
String token = args[index];
index++;
if (token.equals("quit")) {
break;
}
int separator = token.indexOf(':');
if (separator <= 0 || separator == token.length() - 1) {
System.out.println("skip.format=" + token);
continue;
}
String email = token.substring(0, separator);
int age = Integer.parseInt(token.substring(separator + 1));
if (age < 14 || age > 120) {
System.out.println("skip.range=" + token);
continue;
}
savedCount++;
totalAge += age;
System.out.println("saved=" + email + ":" + age);
}
System.out.println("count=" + savedCount);
System.out.println("total=" + totalAge);
}
}saved=java:30
skip.format=bad
skip.range=sql:0
saved=loops:45
count=2
total=75index++를 토큰을 읽은 직후에 두었기 때문에 어느 continue 경로에서도 다음 입력으로 이동합니다.
quit은 break로 반복을 끝내므로 ignored:50은 읽지 않습니다.
저장된 항목만 count와 total을 바꿉니다.
반복 불변식
배치 반복의 각 조건 검사 직전에는 다음 사실이 유지돼야 합니다.
index는 이미 확인한 토큰 수이며 0부터args.length사이입니다.savedCount는 유효해 저장한 항목 수입니다.totalAge는 저장한 회원의 나이만 더한 값입니다.- 현재 토큰이 실패해도
index는 다음 토큰을 가리킵니다.
이처럼 매 회차의 시작과 끝에 유지되는 조건을 반복 불변식이라고 합니다.
출력이 틀렸다면 마지막으로 불변식이 맞았던 회차와 처음 깨진 회차 사이를 확인합니다.
FLOWCHART · LOOP INVARIANT
배치 while의 진행·continue·break 상태 계약
token을 고른 즉시 index를 증가시켜 모든 경로의 진행을 보장하고, 실패는 누적값을 보존한 채 continue하며 quit만 남은 입력을 미도달로 남깁니다.
loop-head invariant 0 ≤ index ≤ args.length이며 count와 total은 유효하게 저장한 항목만 누적합니다.
남은 token 확인
index < args.length가 false면 입력을 모두 확인한 것이므로 최종count와total을 출력합니다.선택 즉시 위치 진전
token = args[index]직후index++를 실행합니다. 따라서 continue와 break를 포함한 모든 처리 경로에서 입력 위치가 진전됩니다.quit은 break
quit이면 반복을 즉시 끝내며 뒤의ignored:50같은 남은 token은 NOT REACHED, 즉 미도달 상태가 됩니다.format·range 실패는 continue
separator 형식이 잘못됐거나 나이가 14–120 밖이면
count와total을 바꾸지 않고 다음 loop head로 돌아갑니다.정수 입력 전제
이 절에서는 나이 문자열이 정수라고 가정합니다. 숫자 변환 실패 복구는
ch3-2에서 다루므로 현재 continue 경로가 아닙니다.유효 token만 저장
검사를 통과한 token만
count++와total += age를 함께 실행한 뒤 다음 loop head로 돌아갑니다.
- 조건·선택·판정 흐름
- aggregate를 보존하는 continue
- 유효 token의 count·total 갱신
배치 반복의 안전성은 continue 위치가 아니라 그 전에 index가 진전됐는지, 그리고 실패 경로가 저장 누적값을 바꾸지 않았는지로 판단합니다.
while과 do-while 선택
| 요구 | 적합한 반복 |
|---|---|
| 입력이 있는 동안만 처리 | while |
| 메뉴를 먼저 한 번 표시 | do-while 가능 |
| 종료 명령까지 계속 | while (true) + break 가능 |
| 잘못된 항목만 건너뜀 | continue |
| 전체 배치를 즉시 중단 | break |
while (true)는 종료 경로가 본문 여러 곳에 있을 때 쓸 수 있지만 모든 break 조건을 찾아야 전체 종료성을 판단할 수 있습니다.
조건에 핵심 상태를 직접 쓸 수 있다면 while (index < args.length)처럼 표현하는 편이 낫습니다.
연습 문제
1부터 시작해 홀수만 합계에 더합니다.
합계가 30을 넘으면 break, 짝수는 continue로 건너뜁니다.
매 회차에서 숫자가 반드시 증가해야 하며 마지막 합계와 종료 숫자를 출력하세요.
해설 보기
public final class OddSumLimit {
public static void main(String[] args) {
int number = 0;
int sum = 0;
while (true) {
number++;
if (number % 2 == 0) {
continue;
}
sum += number;
if (sum > 30) {
break;
}
}
System.out.println("number=" + number);
System.out.println("sum=" + sum);
}
}number=11
sum=36number++가 continue보다 앞에 있어 짝수에서도 진행합니다.
한계 검사는 홀수를 더한 뒤에 있으므로 “30을 넘긴 첫 합계”가 결과입니다.
30 이상이 되기 전에 멈추려면 더하기 전에 다음 합계를 계산해 비교해야 하며 요구가 달라집니다.