본문 바로가기

Java/Java SE, EE

[Java/Java SE, EE] 조건문(if, if-else if-else, switch)

코드 실행 흐름과 흐름 제어

자바 프로그램은 main() 함수에서 시작합니다. main() 함수 내부의 코드는 함수를 감싸는 중괄호({})의 시작에서 끝을 향해 순차적으로 진행됩니다. 이를 코드 실행 흐름이라고 부릅니다.

일반적으로 코드의 실행은 순차적으로 진행되지만, 특정 제어문에 의해 실행 흐름을 제어 할 수 있습니다. 

public static void test() {
   int x = 100;
   
   if (100 > x) {
      System.out.println("이 코드를 실행되지 않습니다.");
   } else {
      System.out.println("이 코드가 실행됩니다.");
   }
}

 

예시에서는 변수 x에 저장된 값이 100이므로, if (100 > x) 을 감싸는 중괄호가 실행되지 않고, 그 다음 중괄호에 해당하는 else 구문이 실행됩니다. 제어문에 의해 코드를 감싸고 있는 중괄호를 블록(Block)이라고 부릅니다.

제어문은 다음 유형을 포함합니다. 이번 포스트에서는 조건문에 해당하는 if 조건문과 switch 조건문에 대해서 알아보겠습니다.

  • 조건문
    • if 조건문
    • switch 조건문
  • 반복문
    • for 반복문
    • while 반복문
    • do-while 반복문

if 조건문

조건식에는 true 또는 false 값을 산출 할 수 있는 연산식이나 boolean 변수 또는 상수가 올 수 있습니다. 조건식에 의해 결정되는 코드 실행은 이어지는 블록에 포함됩니다.

int x = 100;
boolean y = true;

if (100 > x) { }
if (true == y) { }
if (true) { }

예시에서는 true 또는 false 값을 산출하는 연산식(100 > x)과 boolean 변수(true == y), 그리고 boolean 상수(true)에 대한 조건식을 보여줍니다(실제로 boolean 상수를 사용하여 조건식을 구성하는 일은 없을 것입니다). 조건식에 이어지는 블록을 구성하는 코드가 단행인 경우 중괄호를 생략 할 수 있습니다(일반적으로 중괄호를 사용하는 것이 가독성에 도움이 됩니다).

int x = 100;

if (100 > x) System.out.println("이 조건문에 의해 실행되는 블록은 중괄호가 생략되었습니다.");

if 조건문은 else와 else if 조건문을 추가로 포함합니다. 이는 if 조건문을 사용할 때, 각 코드 블록을 서로 다른 조건식을 사용하여 분기 처리하기 위한 용도입니다.

int x = 100;

if (100 > x) {
   System.out.println("100 > x 조건식을 성사하면 이 블록이 실행됩니다.");
} else if (100 < x) {
   System.out.println("100 < x 조건식을 성사하면 이 블록이 실행됩니다.");
} else {
   System.out.println("앞의 두 조건식을 충족하지 않는 경우, 즉 100 == x일 때 이 블록이 실행됩니다.")
}

if-else if-else 조건문을 사용할 때는 다음과 같은 주의 사항이 있습니다.

  • 항상 if 조건문으로 시작해야 한다.
  • else 조건문은 가장 마지막에 위치하며, 생략 될 수 있다.
  • else if 조건문은 if 조건문 이후에 위치하며, 생략 될 수 있다.
  • else if 조건문은 제한 없이 사용 할 수 있다(다양한 else if 조건식을 추가하여 계속 분기시킬 수 있다).
  • 각 블록은 실행되는 코드가 단행인 경우 중괄호({})를 생략 할 수 있다.
  • 어떤 조건식을 충족하여 이어지는 블록이 실행되면, if-else if-else에 의해 이어지는 조건식은 무시하고 조건문을 종료한다.

if 조건식의 복합 조건

if 조건문의 조건식에는 논리 연산자(NOT, AND, OR, XOR)를 포함 할 수 있습니다. 이는 여러 조건들을 논리 연산자를 사용하여 복합적인 조건식으로 구성 할 수 있음을 의미합니다.

아래 코드에서는 두 개의 조건을 논리 곱 연산자(AND)를 통해서 하나의 조건식을 만들고 있습니다. 이 때 자바의 논리 곱 연산자 & 연산자와 && 연산자는 조건의 검사를 계속 할 것인가에 대해서 차이가 있습니다.

int x = 100;
int y = 200;

if (100 <= x && 100 <= y) {
   System.out.println("x가 100보다 크거나 같고, y도 100보다 크거나 같으면 이 블록이 실행됩니다.");
}

A조건(100 <= x)와 B조건(100 <= y)를 & 연산자로 결합했을 때, A 조건을 충족하지 않더라도 B 조건을 검사합니다. 반면 && 연산자를 사용하면 A 조건을 충족하지 않으면 B 조건을 검사하지 않습니다(조건을 하나라도 충족하지 않으면 항상 false). 조건문에서 & 연산자를 사용하는 경우는 지극히 일반적이지 않은 경우이며, 성능적으로 더 우수 할 수 밖에 없는 && 연산자 사용을 권장합니다.

이와 같은 측면에서 비즈니스 로직을 작성 할 때 && 조건문은 조건식을 충족할 가능성이 더 낮은 것을 먼저 검사하는 것이 성능에 유리합니다. 같은 맥락에서 논리 곱 연산자(OR)를 사용하는 || 조건문에서는 조건식을 충족할 가능성이 더 높은 것을 먼저 검사하는 것이 유리합니다.

int x = 100;
int y = 200;

if ((100 <= x) && (100 <= y)) {
   // 조건식 (100 <= x)를 충족하기 더 어려운 경우 먼저 검사하도록 하는 것이 좋습니다.
   // 이어지는 조건을 검사하지 않고 조건문을 종료할 가능성이 더 높기 때문입니다.
}

if ((100 <= y) || (100 <= x)) {
   // 조건식 (100 <= y)를 충족하기 더 쉬운 경우 먼저 검사하도록 하는 것이 좋습니다.
   // 이어지는 조건을 검사하지 않고 조건문을 충족할 가능성이 더 높기 때문입니다.
}

switch 조건문

switch 조건문은 조건식의 논리적 결과가 아닌, 변수 또는 상수의 값에 따라서 분기 처리된다는 점에서 if 조건문과 차이가 있습니다. 예를 들어 정수를 저장하는 변수의 값이 0~9까지 각 숫자와 일치하는 경우에 대한 조건문을 구성하면, if-else if 구문이 총 10개가 필요합니다. 이 경우 switch 조건문으로 대체하면 하나의 조건문을 사용하여 0~9까지 각 숫자에 대한 분기처리가 가능합니다.

int x = 1;

switch (x)
{
   case 0:
      System.out.println("switch 조건문의 x는 0을 저장하고 있습니다.");
      break;
      
   case 1:
      System.out.println("switch 조건문의 x는 1을 저장하고 있습니다.");
      break;
   
   case 2:
      System.out.println("switch 조건문의 x는 2를 저장하고 있습니다.");
      break;
   
   case 3:
      System.out.println("switch 조건문의 x는 3를 저장하고 있습니다.");
      break;
   
   case 4:
      System.out.println("switch 조건문의 x는 4를 저장하고 있습니다.");
      break;
      
   default:
      System.out.println("switch 조건문의 x는 0~4를 제외한 다른 값을 저장하고 있습니다.");
      break;
}

switch 조건문은 case :, default :, break 키워드로 구성됩니다.

  • case :
    조건식을 구성합니다. 이 조건식을 충족하면 break 키워드를 만날 때까지 코드를 실행합니다.
  • default :
    case : 에 의한 조건식을 하나도 충족하지 않을 때 실행됩니다. if-else if-else 조건문에서의 else 키워드에 해당합니다.
  • break
    블록 내에서 이 키워드를 만나면 조건문을 종료합니다.

switch 조건문을 사용 할 때는 break 키워드에 주의해야 합니다. case : 조건식을 충족 할지라도 break 키워드를 만나지 않으면 계속해서 코드를 실행합니다.

int x = 1;

switch (x)
{
   case 0:
      System.out.println("switch 조건문의 x는 0을 저장하고 있습니다.");
      
   case 1:
      System.out.println("switch 조건문의 x는 0 또는 1을 저장하고 있습니다.");
      System.out.println("이 블록은 x가 0을 저장하고 있을 때도 실행됩니다.");
   
   case 2:
      System.out.println("switch 조건문의 x는 0 또는 1, 2를 저장하고 있습니다.");
      System.out.println("이 블록은 x가 0을 저장하고 있을 때도 실행됩니다.");
      System.out.println("이 블록은 x가 1을 저장하고 있을 때도 실행됩니다.");
      break;
      
   default:
      System.out.println("switch 조건문의 x는 0~2를 제외한 다른 값을 저장하고 있습니다.");
      break;
}

 

위의 예시에 의한 실행 결과는 다음과 같습니다. 예시에서 case 1:에 의한 두 줄의 코드 실행을 의도하였으나, 블록 내 break 키워드가 누락되었기 때문에 다음 break 키워드를 만나는 case 2:의 블록 내 모든 코드를 실행하고 조건문을 종료합니다. 이와 같은 실수가 switch 조건문으로 인해 종종 발생하는 버그가 됩니다.

switch 조건문의 x는 0 또는 1을 저장하고 있습니다.
이 블록은 x가 0을 저장하고 있을 때도 실행됩니다.
switch 조건문의 x는 0 또는 1, 2를 저장하고 있습니다.
이 블록은 x가 0을 저장하고 있을 때도 실행됩니다.
이 블록은 x가 1을 저장하고 있을 때도 실행됩니다.

위의 예시에서는 블록 범위를 표현하기 위해 중괄호를 사용하고 있지 않습니다. if 조건문과 달리, switch 조건문에 의해 실행되는 코드가 여러 줄이어도 하나의 블록으로 취급합니다. 다음과 같이 중괄호를 사용하여 블록 범위를 명시적으로 표시 할 수 있으며, break 키워드를 중괄호 내부 혹은 외부에 있더라도 실행 결과는 동일합니다.

switch (x)
{
   case 0:
      {
         System.out.println("블록을 중괄호를 사용하여 명시적으로 표현하였습니다.");
      }
      break;
      
   default:
      {
         System.out.println("블록을 중괄호를 사용하여 명시적으로 표현하였습니다.");
         break;
      }
}

마지막으로 조건식에 포함되는 변수 또는 상수는 정수 뿐만 아니라 자바의 char와 String 타입 역시 사용 할 수 있습니다.

char x = 'a';

switch (x)
{
   case 'a':
      System.out.println("변수 x는 'a' 문자를 저장하고 있습니다.");
      break;
      
   case 'b':
      System.out.println("변수 x는 'b' 문자를 저장하고 있습니다.");
      break;
      
   case 'c':
      System.out.println("변수 x는 'c' 문자를 저장하고 있습니다.");
      break;
      
   default:
      break;
}