4.3 흐름 제어문 (Control Flow Statements)
반복문을 조절하기 위해 자바는 흐름을 직접 제어할 수 있는 특별한 키워드를 제공합니다. break, continue, return을 활용하면 반복 횟수를 동적으로 조절하고 메서드를 즉시 종료할 수 있습니다.
1. break 문
break문을 만나면 자신이 포함된 가장 가까운 반복문(또는 switch문)을 완전히 탈출 합니다.
기본 사용법
// 합이 100을 초과하는 시점 찾기
int sum = 0;
int i = 0;
while (true) { // 무한 루프
i++;
sum += i;
if (sum > 100) {
break; // 조건 충족 시 반복문 탈출
}
}
System.out.println("i = " + i + ", sum = " + sum);
// 출력: i = 14, sum = 105
for 문에서의 break
int[] numbers = {3, 7, 1, 9, 5, 2, 8};
int target = 9;
int foundIndex = -1;
for (int i = 0; i < numbers.length; i++) {
if (numbers[i] == target) {
foundIndex = i;
break; // 찾으면 더 이상 순회할 필요 없음
}
}
if (foundIndex != -1) {
System.out.println(target + "를 인덱스 " + foundIndex + "에서 찾았습니다.");
} else {
System.out.println(target + "를 찾지 못했습니다.");
}
// 출력: 9를 인덱스 3에서 찾았습니다.
2. break 레이블 (Label)
중첩 반복문에서 break는 기본적으로 가장 가까운 반복문만 탈출합니다. 레이블(label) 을 사용하면 중첩 반복문의 외부 반복문까지 한 번에 탈출할 수 있습니다.
레이블 없는 break의 한계
// 레이블 없이는 안쪽 반복문만 탈출
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == 1 && j == 1) {
break; // 안쪽 for문만 탈출, 바깥 for문은 계속 실행
}
System.out.println("i=" + i + ", j=" + j);
}
}
// i=1, j=1을 만나도 i=2 반복이 계속 실행됨
레이블 break로 중첩 탈출
// 레이블 break: 바깥쪽 반복문까지 탈출
outer: // 레이블 선언 (관례상 소문자 사용)
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == 1 && j == 1) {
break outer; // outer 레이블이 붙은 for문까지 탈출
}
System.out.println("i=" + i + ", j=" + j);
}
}
System.out.println("반복문 완전 탈출");
출력:
i=0, j=0
i=0, j=1
i=0, j=2
i=1, j=0
반복문 완전 탈출
실전 활용: 2차원 배열에서 값 찾기
public class SearchIn2DArray {
public static void main(String[] args) {
int[][] matrix = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
int target = 7;
int row = -1, col = -1;
search:
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
if (matrix[i][j] == target) {
row = i;
col = j;
break search; // 찾으면 이중 반복문 전체 탈출
}
}
}
if (row != -1) {
System.out.printf("값 %d는 [%d][%d]에 있습니다.%n", target, row, col);
}
// 출력: 값 7는 [1][2]에 있습니다.
}
}
3. continue 문
continue문을 만나면 현재 반복의 남은 코드를 건너뛰고 다음 반복으로 바로 진행 합니다. break와 달리 반복문 자체를 벗어나지 않습니다.
기본 사용법
// 1~10 중 홀수만 출력
for (int i = 1; i <= 10; i++) {
if (i % 2 == 0) {
continue; // 짝수이면 아래 출력문을 건너뛰고 i++ 후 다음 반복
}
System.out.print(i + " "); // 홀수만 실행
}
// 출력: 1 3 5 7 9
continue 작동 방식 상세
for (int i = 0; i <= 5; i++) {
System.out.print("시작(" + i + ") ");
if (i == 3) {
continue; // i==3일 때 아래 "끝" 출력을 건너뜀
}
System.out.print("끝(" + i + ") ");
}
출력:
시작(0) 끝(0) 시작(1) 끝(1) 시작(2) 끝(2) 시작(3) 시작(4) 끝(4) 시작(5) 끝(5)
i=3일 때 continue로 "끝(3)"이 출력되지 않습니다.
while 문에서의 continue 주의사항
// 주의: while문에서 continue 사용 시 증감식을 continue 앞에 배치해야 함
int i = 0;
while (i < 10) {
i++; // 증감식을 먼저!
if (i % 2 == 0) {
continue; // 짝수면 건너뜀. 증감식이 위에 있으므로 무한 루프 없음
}
System.out.print(i + " ");
}
// 출력: 1 3 5 7 9
while + continue 무한 루프 주의
int i = 0;
while (i < 10) {
if (i % 2 == 0) {
continue; // i가 0일 때 continue → 다시 조건식 검사 → i는 여전히 0 → 무한 루프!
}
i++;
}
continue 이후 증감식이 실행되지 않아 무한 루프가 발생합니다. while문에서는 증감식 위치에 주의하세요.
4. continue 레이블
레이블을 사용하면 중첩 반복문에서 외부 반복문의 다음 반복으로 이동 할 수 있습니다.
outer:
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (j == 2) {
continue outer; // 안쪽 루프를 건너뛰고 outer의 다음 반복(i++)으로 이동
}
System.out.print("[" + i + "," + j + "] ");
}
System.out.println(); // j==2로 인해 이 줄은 실행되지 않음
}
출력:
[0,0] [0,1] [1,0] [1,1] [2,0] [2,1] [3,0] [3,1]
5. return 문
return문은 현재 실행 중인 메서드를 즉시 종료 합니다. 메서드 중간에서 조건에 따라 일찍 종료해야 할 때 유용합니다.
void 메서드에서의 return
public class ReturnExample {
// void 메서드에서 return은 값 없이 단독 사용
static void printPositive(int number) {
if (number <= 0) {
System.out.println("양수가 아닙니다.");
return; // 메서드 즉시 종료
}
// number가 양수일 때만 여기까지 도달
System.out.println("양수: " + number);
}
public static void main(String[] args) {
printPositive(5); // 출력: 양수: 5
printPositive(-3); // 출력: 양수가 아닙니다.
printPositive(10); // 출력: 양수: 10
}
}
반환값이 있는 메서드에서의 return
// 두 수 중 더 큰 수를 반환하는 메서드
static int max(int a, int b) {
if (a > b) {
return a; // a가 크면 즉시 a를 반환하고 종료
}
return b; // 그렇지 않으면 b를 반환
}
// 조기 반환(early return) 패턴으로 중첩 줄이기
static String getGrade(int score) {
if (score < 0 || score > 100) return "오류"; // 유효성 검사
if (score >= 90) return "A";
if (score >= 80) return "B";
if (score >= 70) return "C";
if (score >= 60) return "D";
return "F";
}
6. 실전 예제 1: 소수 목록 출력 (break/continue 활용)
public class PrimeList {
public static void main(String[] args) {
int limit = 30;
System.out.print(limit + " 이하의 소수: ");
for (int n = 2; n <= limit; n++) {
boolean isPrime = true;
for (int i = 2; i * i <= n; i++) { // √n까지만 검사
if (n % i == 0) {
isPrime = false;
break; // 약수 발견: 더 검사 불필요, 안쪽 반복문 탈출
}
}
if (!isPrime) {
continue; // 소수가 아니면 출력 건너뜀
}
System.out.print(n + " ");
}
System.out.println();
}
}
출력:
30 이하의 소수: 2 3 5 7 11 13 17 19 23 29
7. 실전 예제 2: 숫자 맞추기 게임 (break + Scanner)
import java.util.Random;
import java.util.Scanner;
public class NumberGuessingGame {
public static void main(String[] args) {
Random random = new Random();
Scanner scanner = new Scanner(System.in);
int answer = random.nextInt(100) + 1; // 1~100 랜덤 숫자
int attempts = 0;
int maxAttempts = 10;
System.out.println("1~100 사이의 숫자를 맞혀보세요! (최대 " + maxAttempts + "번)");
while (attempts < maxAttempts) {
System.out.print("추측: ");
int guess = scanner.nextInt();
attempts++;
if (guess < 1 || guess > 100) {
System.out.println("1~100 사이의 숫자를 입력하세요.");
attempts--; // 잘못된 입력은 횟수에서 제외
continue;
}
if (guess == answer) {
System.out.println("정답! " + attempts + "번 만에 맞혔습니다!");
break; // 정답이면 반복문 탈출
} else if (guess < answer) {
System.out.println("더 큰 숫자입니다. 남은 기회: " + (maxAttempts - attempts) + "번");
} else {
System.out.println("더 작은 숫자입니다. 남은 기회: " + (maxAttempts - attempts) + "번");
}
}
if (attempts >= maxAttempts) {
System.out.println("게임 오버! 정답은 " + answer + "였습니다.");
}
scanner.close();
}
}
실행 예시:
1~100 사이의 숫자를 맞혀보세요! (최대 10번)
추측: 50
더 큰 숫자입니다. 남은 기회: 9번
추측: 75
더 작은 숫자입니다. 남은 기회: 8번
추측: 63
정답! 3번 만에 맞혔습니다!
고수 팁
흐름 제어문 사용 원칙:
- break는 명확한 종료 조건이 있을 때- 무한루프에서 탈출할 때 반드시
break조건을 문서화하세요. - continue는 필터링에 유용- 조건에 맞지 않는 항목을 건너뛸 때
if문 중첩보다continue가 더 명확합니다. - 레이블은 최후의 수단- 레이블은 가독성을 해칩니다. 가능하면 메서드로 분리하는 것이 더 좋습니다.
- early return으로 중첩 제거- 메서드 시작 부분에서 유효하지 않은 케이스를
return으로 처리하면 코드 중첩이 줄어듭니다.
// 중첩이 많은 방식
void process(String input) {
if (input != null) {
if (!input.isEmpty()) {
if (input.length() > 3) {
// 실제 처리
}
}
}
}
// early return으로 개선
void process(String input) {
if (input == null) return;
if (input.isEmpty()) return;
if (input.length() <= 3) return;
// 실제 처리
}