같은 코드를 여러 번 실행해야 할 때 직접 복사해서 작성하면 코드가 길어지고 수정도 어려워진다. 반복문은 이런 반복 작업을 하나의 구조로 표현한다.
while문의 기본 구조int page = 1;
while (page <= 3) {
System.out.println("페이지 요청: " + page);
page++;
}
출력:
페이지 요청: 1
페이지 요청: 2
페이지 요청: 3
while은 조건식이 true인 동안 블록을 반복한다.
실행 순서는 다음과 같다.
조건 확인
↓ true
블록 실행
↓
조건 확인
↓ false
종료
int count = 1;
while (count <= 3) {
System.out.println(count);
}
count를 증가시키지 않았기 때문에 조건이 계속 참이다.
반복문을 작성할 때는 다음 세 가지를 확인하면 좋다.
do-whilewhile은 처음부터 조건이 거짓이면 한 번도 실행되지 않는다.
반면 do-while은 블록을 먼저 실행한 뒤 조건을 검사한다.
int retryCount = 0;
do {
System.out.println("API 요청 시도");
retryCount++;
} while (retryCount < 1);
최소 한 번은 반드시 실행해야 하는 로직에 사용할 수 있다.
break반복문을 즉시 종료한다.
int number = 1;
while (true) {
if (number == 5) {
break;
}
System.out.println(number);
number++;
}
number == 5가 되면 반복문 전체를 빠져나온다.
continue현재 반복의 남은 코드를 건너뛰고 다음 반복으로 이동한다.
for (int i = 1; i <= 10; i++) {
if (i % 2 == 0) {
continue;
}
System.out.println(i);
}
홀수만 출력된다.
for문반복 횟수가 명확한 경우 for가 특히 편하다.
for (int i = 0; i < 5; i++) {
System.out.println("요청 번호: " + i);
}
구조:
for (초기식; 조건식; 증감식) {
실행 코드
}
실행 흐름은 다음과 같다.
초기식
↓
조건식 ─ false → 종료
↓ true
본문
↓
증감식
└────→ 조건식
for와 while은 언제 사용할까?for횟수나 인덱스가 중심일 때 읽기 좋다.
for (int i = 0; i < 10; i++) {
// ...
}
while언제 끝날지 횟수보다 조건이 중요한 경우 자연스럽다.
while (serverIsRunning) {
// 요청 처리
}
문법적으로는 서로 변환 가능한 경우가 많다. 중요한 것은 어떤 구조가 의도를 더 명확하게 보여주는지다.
반복문 안에 반복문을 넣을 수 있다.
for (int row = 1; row <= 3; row++) {
for (int column = 1; column <= 4; column++) {
System.out.println("row=" + row + ", column=" + column);
}
}
바깥 반복문이 3번, 안쪽 반복문이 각 4번 실행되므로 본문은 총 12번 실행된다.
중첩이 깊어지면 실행 횟수가 빠르게 늘어날 수 있으므로 데이터 크기가 커질 때는 성능도 생각해야 한다.
print와 println패턴 출력처럼 같은 줄에 데이터를 이어서 보여주고 싶다면 print를 사용할 수 있다.
for (int i = 1; i <= 5; i++) {
System.out.print(i + " ");
}
1 2 3 4 5
조건을 복잡하게 한 줄에 몰아넣기보다 변수와 메서드 이름으로 의도를 드러내는 것이 좋다.
for (int attempt = 1; attempt <= maxRetryCount; attempt++) {
// 재시도 로직
}
i, j 같은 짧은 이름은 단순 인덱스 루프에서는 자연스럽지만 비즈니스 의미가 있다면 의미 있는 이름을 사용하는 편이 낫다.
for문의 실행 순서를 코드로 풀어보기for (int i = 0; i < 3; i++) {
System.out.println(i);
}
이 코드는 다음 순서로 움직인다.
1. int i = 0 // 최초 한 번
2. i < 3 // 조건 검사
3. 본문 실행
4. i++ // 증감식
5. 다시 조건 검사
조건이 false가 되는 순간 반복문을 빠져나간다.
다음은 문법적으로 가능한 무한 반복이다.
for (;;) {
// 계속 반복
}
초기식, 조건식, 증감식은 생략할 수 있지만 이들을 구분하는 세미콜론은 남아 있어야 한다.
실제로는 무한 반복 의도가 더 분명한 다음 형태도 자주 사용한다.
while (true) {
// ...
}
for (int i = 0; i < 5; i++) {
System.out.println(i);
}
// System.out.println(i); // 컴파일 오류
for의 초기식에서 선언한 i는 반복문 안에서만 존재한다.
반면 다음 코드는 i의 범위가 더 넓다.
int i = 0;
while (i < 5) {
i++;
}
카운터가 반복문에서만 필요하다면 for문 안에 선언해 스코프를 좁히는 것이 명확하다.
break와 continue는 흐름이 완전히 다르다for (int i = 1; i <= 5; i++) {
if (i == 3) {
continue;
}
System.out.println(i);
}
continue는 현재 반복의 나머지 부분만 건너뛰므로 1, 2, 4, 5가 출력된다.
for (int i = 1; i <= 5; i++) {
if (i == 3) {
break;
}
System.out.println(i);
}
break는 반복문 자체를 종료하므로 1, 2까지만 출력된다.
for (int row = 0; row < 3; row++) {
for (int column = 0; column < 4; column++) {
System.out.println(row + ", " + column);
}
}
바깥 반복 3회 × 안쪽 반복 4회 = 내부 코드는 총 12번 실행된다.
데이터가 커지면 이런 곱셈 구조가 성능에도 영향을 줄 수 있다. 입문 단계에서는 최소한 중첩될수록 총 실행 횟수가 곱해진다는 감각을 익혀두자.
패턴 출력이나 중첩 반복이 이해되지 않을 때는 종이나 로그에 i, j 값을 적는다.
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= i; j++) {
System.out.print("*");
}
System.out.println();
}
실행을 표로 적으면 구조가 보인다.
i | j가 가지는 값 | 출력 |
|---|---|---|
| 1 | 1 | * |
| 2 | 1, 2 | ** |
| 3 | 1, 2, 3 | *** |
IDE 디버거를 배우기 전에도 충분히 효과적인 방법이다.
int[] values = {10, 20, 30};
for (int i = 0; i <= values.length; i++) {
System.out.println(values[i]);
}
배열의 마지막 인덱스는 length - 1이므로 <=를 사용하면 범위를 한 칸 넘어간다.
for (int i = 0; i < values.length; i++) {
System.out.println(values[i]);
}
반복문에서는 시작값, 종료 조건, 증감 방향을 세트로 확인하는 습관이 중요하다.
while은 조건이 참인 동안 반복한다.do-while은 조건과 관계없이 최소 한 번 실행된다.break는 반복문 전체를 즉시 종료한다.continue는 현재 반복만 건너뛴다.for는 반복 횟수나 카운터가 명확할 때 읽기 좋다.