linuxism 2012. 4. 19. 17:40

1. 조건문 - if, switch
  
조건문은 조건식과 문장을 포함하는 블럭{}으로 구성되어 있으며, 조건식의 연산결과에 따라 실행될 문장을 달리 할 수 있다. 
처리해야할 경우의 수가 많을 때는 switch문을 사용해서 표현할 수 있는지 살펴봐야 한다. 
[참고] 모든 switch문은 if문으로 변경이 가능하지만, 모든 if문이 switch문으로 변경 가능한 것은 아니다. 


1.1 if문

if문은 널리 사용되는 조건문이며, 기본구조는 다음과 같다. 


if (조건식) { 
      // 조건식의 연산결과가 true일 때 수행될 문장들을 적는다. 



if 다음에 오는 조건식에는 연산의 최종결과 값이 true 또는 false인 수식만을 사용할 수 있다. 조건식의 결과가 false이면, 블럭{} 내의 문장이 실행되지 않는다. 

[참고] C언어에서는 조건식의 최종결과 값으로 true 또는 false 이외의 값을 허용하지만, 자바에서는 이를 허용하지 않는다. 

if문의 변형인 if-else문의 기본 구조는 다음과 같다. if문에 else블럭을 추가한 것이다. else블럭 내의 문장들은 if문의 조건식의 결과가 false일 때 수행되는 문장들이다. 조건식의 결과에 따라 이 두 개의 블럭 중 어느 한 블럭의 내용만 수행하고 전체 if문을 벗어나게 된다. 


if (조건식) { 
      // 조건식의 연산결과가 true일 때 수행될 문장들을 적는다. 
else { 
      // 조건식의 연산결과가 false일 때 수행될 문장들을 적는다. 



if문의 또 다른 변형으로 if-else if가 있는데 기본 구조는 다음과 같다. 여러 개의 블럭 중 조건식을 만족하는 하나의 블럭만을 수행하고 if문 전체를 빠져나가게 된다. 
if-else if의 기본구조는 다음과 같다.


if (조건식1) { 
      // 조건식1의 연산결과가 true일 때 수행될 문장들을 적는다. 
else if (조건식2) { 
      // 조건식2의 연산결과가 true일 때 수행될 문장들을 적는다. 
else if (조건식3) {       // 여러 개의 else if를 사용할 수 있다. 
      // 조건식3의 연산결과가 true일 때 수행될 문장들을 적는다. 
      //... 
else {       // 보통 else블럭으로 끝나며, else블럭은 생략이 가능하다. 
      // 위의 어느 조건식도 만족하지 않을 때 수행될 문장들을 적는다. 



블럭{}은 여러 개의 문장을 하나로 묶기 위해 사용되는 것이며, 함수, 조건문, 반복문 등에 사용된다.조건문과 반복문에서는 수행될 문장이 하나인 경우 블럭을 생략할 수 있으나, 가능하면 생략 않고 사용하는 것이 바람직하다.
수행될 문장이 한 문장이라서 블럭을 생략하고 적었을 때, 나중에 새로운 문장들이 추가되면 블럭으로 문장들을 감싸 주어야 하는데 이 때 블럭을 추가하는 것을 잊기 쉽기 때문이다. 그리고, 여러 개의 if문이 중첩되어 사용되었을 때 if문과 else블럭의 관계가 의도한 바와 다르게 형성될 수도 있다. 

[예제4-1] FlowEx1.java

class FlowEx1 

      public static void main(String[] args) 
      { 
            int visitCount = 0; 
            if (visitCount < 1) { 
                  System.out.println("처음 오셨군요. 방문해 주셔서 감사합니다.") ; 
            } 
      } 

[실행결과]
처음 오셨군요. 방문해 주셔서 감사합니다. 

[예제4-2] FlowEx2.java

class FlowEx2 

      public static void main(String[] args) 
      { 
            int visitCount = 5; 
            if (visitCount < 1) {       // 5 < 1의 연산결과는 false. 
                  System.out.println("처음 오셨군요. 방문해 주셔서 감사합니다.") ; 
            } else { 
                  System.out.println("다시 방문해 주셔서 감사합니다.") ; 
           } 
           System.out.println("방문횟수는 " + ++visitCount + "번 입니다."); 
      } 

[실행결과]
다시 방문해 주셔서 감사합니다. 
방문횟수는 6번 입니다. 

[예제4-3] FlowEx3.java

class FlowEx3 

      public static void main(String[] args) 
      { 
            int score = 45; 
            char grade ='\u0000'; 

            if (score >= 90)                   // score가 90점 보다 같거나 크면 A학점(grade) 
            { 
                        grade = 'A';             
            } else if (score >=80){             // score가 80점 보다 같거나 크면 B학점(grade) 
                        grade = 'B'; 
            } else {                         // 나머지는 C학점(grade) 
                        grade = 'C'; 
      } 
            
            System.out.println("당신의 학점은 " + grade + "입니다."); 
      } 

[실행결과]
당신의 학점은 C입니다. 

if문은 삼항연산자(? :)로 바꿀 수 있는 경우가 많이 있는데, 간단한 if문의 경우 삼항연산자를 사용한 문장들로 변경하는 것을 고려해보도록 한다. 
위의 예제에서 if문 대신 삼항연산자를 사용하면 다음과 같다. 

[예제4-4] FlowEx4.java

class FlowEx4 

      public static void main(String[] args) 
      { 
            int score = 45; 
            char grade ='\u0000'; 
            grade = (score >=90) ? 'A' : ((score >=80) ? 'B' : 'C'); 
            
            System.out.println("당신의 학점은 " + grade + "입니다."); 
      } 

[실행결과]
당신의 학점은 C입니다. 

두 예제는 모두 같은 결과를 얻지만, 삼항연산자를 사용한 두 번째 예제가 보다 더 간결하다. 이렇듯 삼항연산자를 활용하면, if문을 간단히 표현할 수 있다. 



1.2 중첩 if문

if문의 블럭 내에 또 다른 if문을 사용하는 것이 가능하며, 이것을 중첩 if문이라고 부른다. 


if (조건식1) { 
      // 조건식1의 연산결과가 true일 때 수행될 문장들을 적는다. 
      if (조건식2) { 
            // 조건식1과 조건식2가 모두 true일 때 수행될 문장들 
      } else { 
            // 조건식1이 true이고, 조건식2가 false일 때 수행되는 문장들 
      } 
else { 
      // 조건식1이 false일 때 수행되는 문장들 



[예제4-5] FlowEx5.java

class FlowEx5 

      public static void main(String[] args) 
      { 
            int score = 82; 
            String grade ="";                   // 두 문자를 저장할 것이므로 String으로 했음 

           System.out.println("당신의 점수는 " + score + "입니다."); 
            if (score >= 90)                   // score가 90점 보다 같거나 크면 A학점(grade) 
            { 
                        grade = "A"
                        if ( score >= 98) { // 90점 이상 중에서도 98점 이상은 A+ 
                                    grade += "+";       // grade = grade + "+";와 같다. 
                        } else if ( score < 94)       { 
                                    grade += "-"
                        } 
            } else if (score >= 80){       // score가 80점 보다 같거나 크면 B학점(grade) 
                        grade = "B"
                        if ( score >= 88) { 
                                    grade += "+"
                        } else if ( score < 84)       { 
                                    grade += "-"
                        } 

      } else {                         // 나머지는 C학점(grade) 
                        grade = "C"
      } 
            
            System.out.println("당신의 학점은 " + grade + "입니다."); 
      } 

[실행결과]
당신의 점수는 82입니다. 
당신의 학점은 B-입니다. 

위 예제에서 보듯이, 모두 3개의 if문으로 이루어져 있으며 if문 내의 블럭에 또 다른 2개의 if문을 포함하고 있는 모습을 하고 있다. 제일 바깥쪽에 있는 if문에서 점수에 따라 학점(grade)을 결정하고, 내부의 if문에서는 학점을 더 세부적으로 나누어서 평가를 하고 그 결과를 출력한다. 



1.3 switch문


조건의 경우의 수가 많을 때는 if문 보다 switch문을 사용하는 것이 더 간결하고 알아보기 쉽다. if문은 조건식의 결과가 true, false 두 가지 밖에 없기 때문에 경우의 수가 많아 질수록 계속 else-if를 추가해야하므로 조건식이 많아져서 복잡해지고, 여러 개의 조건식을 계산해야하므로 수행시간도 많이 걸린다. 하지만, switch문의 조건식은 결과값으로 int형 범위의 정수값을 허용하므로, 하나의 조건식만 계산하면 그 결과에 따라서 해당 문장들을 수행하면 되므로 같은 기능의 if문보다 속도가 빠르다. 

하지만, switch문은 if문 보다 제약조건이 많기 때문에, 조건식을 switch문으로 작성할 수 없는 경우가 많다. 

switch문의 기본구조는 아래와 같다. 


switch (조건식) { 
      case 값1 : 
            // 조건식의 결과가 값1과 같을 경우 수행될 문장들 
            //... 
            break; 
      case 값2 : 
            // 조건식의 결과가 값2와 같을 경우 수행될 문장들 
            break; 
            //... 
      //... 
      default : 
            // 조건식의 결과와 일치하는 case문이 없을 때 수행될 문장들 
            //... 



switch문의 조건식은 연산결과가 int형 범위의 정수값이어야한다. byte, short, char, int 타입의 변수나 리터럴을 사용할 수 있다. 그리고, case문에는 반드시 상수값만을 허용한다. 변수는 허용되지 않으므로 유의해야한다. 
switch문의 조건식을 먼저 계산한 다음, 그 결과와 일치하는 case문으로 이동하다. 이동한 case문 이하에 있는 문장들을 수행하며, break문을 만나면 전체 switch문을 빠져나가게 된다. 만일 case문 아래에 break문을 생략하면, 다른 break문을 만나거나 switch문 블럭의 끝을 만날 때까지 나오는 모든 문장들을 수행한다. 

[예제4-6] FlowEx6.java

class FlowEx6 

      public static void main(String[] args) 
      { 
            // Math클래스의 random()함수를 이용해서 1~10사이의 수를 임의로 얻어낼 수 있다. 
            int score = (int)(Math.random() * 10) + 1; 

            switch(score*100) { 
                  case 100 : 
                        System.out.println("당신의 점수는 100이고, 상품은 자전거입니다.");                   
                        break; 
                  case 200 : 
                        System.out.println("당신의 점수는 200이고, 상품은 TV입니다.");                   
                        break; 
                  case 300 : 
                        System.out.println("당신의 점수는 300이고, 상품은 노트북 컴퓨터입니다.");                                     break; 
                  case 400 : 
                        System.out.println("당신의 점수는 400이고, 상품은 자동차입니다.");                   
                        break; 
                  default : 
                        System.out.println("죄송하지만 당신의 점수에 해당하는 상품이 없습니다."); 
            } 
      } 

[실행결과]
당신의 점수는 100이고, 상품은 자전거입니다.

Math클래스의 random메서드를 사용해서 1과 10사이의 임의의 수를 얻은 다음, 그 값을 score에 저장한다. score에 100을 곱한 값과 일치 하는 case문을 찾아서 이동한다. 
예를 들어서 score값이 1이라면, score * 100의 결과는 100이 되고, 첫 번째 case문인 case 100:으로 이동하여 System.out.println("당신의 점수는 100이고, 상품은 자전거입니다.")문장을 수행한 후, break문을 만나서 switch문을 빠져 나오게 된다. 
이 예제에서는 switch문의 조건식인 score * 100의 결과가, 100, 200, 300, 400인 경우에 대해서만 다루고 있기 때문에 그 외의 값인 500, 600, 700, 800, 900, 1000의 경우에는 default문 이하의 문장들 수행된다. 
[참고]random메서드를 사용했기 때문에 매번 수행할 때마다 다른 결과를 얻게 될 것이다. 

random메서드는 0.0과 1.0사이의 값 중 하나의 double값을 생성한다.(0.0은 범위에 포함되고 1.0은 포함되지 않는다.) 


0.0 <= Math.random() < 1.0 


예를 들어 1 과 10 사이의 정수를 구하기를 원한다면 아래와 같은 순서를 따르도록 한다. 

1. 각 변에 10을 곱한다. 


0.0 * 10 <= Math.random() * 10 < 1.0 * 10 
0.0 <= Math.random() * 10 < 10.0 


2. 각 변을 int형으로 변환한다. 


(int)0.0 <= (int)(Math.random() * 10) < (int)10.0 
0 <= (int)(Math.random() * 10) < 10 


지금까지는 0과 9사이의 정수 중 하나를 가질 수 있다.(0은 포함, 10은 포함 안됨) 

3. 각 변에 1을 더한다. 


0+1 <= (int)(Math.random() * 10) +1 < 10 +1 
1 <= (int)(Math.random() * 10) +1 < 11 


자, 이제는 1과 10사이의 정수 중 하나를 얻을 수 있다.(1은 포함, 11은 포함 안됨) 
[참고]순서 2와 3을 바꿔서, 각 변에 1을 먼저 더한 다음 int형으로 변환해도 같은 결과를 얻는다. 

위와 같은 방식으로 식을 변환해가며 범위를 조절하면 원하는 범위의 값을 얻을 수 있다. 주사위를 던졌을 때 나타나는 임의의 값을 얻기 위해서는 10대신 6을 곱하면 된다. 그렇게 하면 1과 6사이의 값을 얻어낼 수 있을 것이다. 

random메서드의 활용 예를 한가지 더 소개하자면, 숫자 대신 임의의 문자를 얻을 수 있도록 하는 것이다. 대문자 A~Z사이의 문자 중 임의의 한 문자를 얻도록 하려면 다음과 같이 한다. 
[참고]대문자 26개의 코드값은 65부터 90까지(A~Z)이다. 



0.0 <= Math.random() < 1.0 



1. 발생시키려는 수의 개수가 26개 이므로 각 변에 26을 곱한다. 


0.0 * 26 <= Math.random() * 26 < 1.0 * 26 
0.0 <= Math.random() * 26 < 26.0 


2. 65부터 시작하므로 각 변에 65를 더한다. 


65.0 <= Math.random() * 26 + 65 < 91.0 


3. 각 변을 문자형(char형)으로 형변환을 한다. 


(char)65.0 <= (char)(Math.random() * 26 +65) < (char)91.0 
'A' <= (char)(Math.random() * 26 +65) < '[' 


[참고] '['는 대문자 Z다음에 오는 문자.(코드값이 Z보다 1이 더 큰 문자). 

여기서 주의해야할 것은 위의 예에서와는 달리 char형으로 형변환한 후에 65를 더하면 전체 결과가 int형이 되어 버리기 때문에 char형 값을 얻을 수 없다. 그렇기 때문에 65를 먼저 더하고 그 다음에 형변환을 해야 하는 것이다. 

[예제4-7] FlowEx7.java

class FlowEx7 

      public static void main(String[] args) 
      { 
            char ch = (char)(Math.random() * 4 +65); // A, B, C, D중의 하나를 얻을 수 있다. 
            int score = 0; 
            
            switch (ch) 
            { 
                  case 'A': 
                        score = 90; 
                        break; 
                  case 'B': 
                        score = 80; 
                        break; 
                  case 'C': 
                        score = 70; 
                        break; 
                  case 'D': 
                        score = 60; 
                        break; 
            } 

            System.out.println("당신의 점수는 "+ score +"점 이상 입니다.");             
      } 

[실행결과]
당신의 점수는 80점 이상 입니다. 

[참고]Math.random()를 사용했기 때문에 실행결과가 이와 다를 수 있다. 

[예제4-8] FlowEx8.java

class FlowEx8 

      public static void main(String[] args) 
      { 
            int score = 1; 

            switch(score*100) { 
                  case 100 : 
                        System.out.println("당신의 점수는 100이고, 상품은 자전거입니다.");                   
                  case 200 : 
                        System.out.println("당신의 점수는 200이고, 상품은 TV입니다.");                   
                  case 300 : 
                        System.out.println("당신의 점수는 300이고, 상품은 노트북 컴퓨터입니다."); 
                  case 400 : 
                        System.out.println("당신의 점수는 400이고, 상품은 자동차입니다.");                   
                  default : 
                        System.out.println("죄송하지만 당신의 점수에 해당하는 상품이 없습니다."); 
            } 
      } 

[실행결과]
당신의 점수는 100이고, 상품은 자전거입니다. 
당신의 점수는 200이고, 상품은 TV입니다. 
당신의 점수는 300이고, 상품은 노트북 컴퓨터입니다. 
당신의 점수는 400이고, 상품은 자동차입니다. 
죄송하지만 당신의 점수에 해당하는 상품이 없습니다. 

위의 예제는 FlowEx6.java를 변형한 것으로 score의 값을 1로 고정시키고, switch문에 있는 break문을 모두 뺀 것이다. 
score의 값이 1이므로 switch문의 조건식 결과 값은 100이 된다. 그래서 case 100:으로 이동한 후 그 이하의 문장들을 수행한다. FlowEx6.java과는 달리, break문이 없으므로 case 100:이후부터 switch문의 블럭 끝까지 모든 문장들을 수행하게 된다. 
그렇기 때문에 매 case문마다 break문을 사용하는 것을 잊지 않도록 한다. 때로는 switch문의 이러한 성질을 이용하기도 한다. 

[예제4-9] FlowEx9.java

class FlowEx9 

      public static void main(String[] args) 
      { 
            int score = (int)(Math.random() * 10) + 1; 
            String msg =""

            score *= 100;             // score = score * 100; 

            msg = "당신의 점수는 " + score + "이고, 상품은 "
      
            switch(score) { 
                  case 1000 : 
                        msg += "자전거, ";             // msg = msg + "자전거, "; 
                  case 900 : 
                        msg += "TV, ";                   
                  case 800 : 
                        msg += "노트북 컴퓨터, ";                   
                  case 700 : 
                        msg += "자전거, ";                   
                  default : 
                        msg += "볼펜"
            } 

            System.out.println( msg + "입니다."); 
      } 

[실행결과]
당신의 점수는 800이고, 상품은 노트북 컴퓨터, 자전거, 볼펜입니다. 

Math클래스의 random()을 사용했기 때문에 위 예제의 결과는 실행할 때마다 결과가 다를 것이다. 위의 결과는 점수가 800점이 되었을 때의 결과이다. 점수(score)는 100부터 1000점까지 100점 단위의 값을 갖을 수 있는데, 높은 점수를 얻을수록 많은 상품을 주도록 되어 있다. 예를 들어 1000점을 얻은 사람은 모든 상품을 다 가질 수 있도록 되어 있다. 
이 예제에서 알 수 있듯이 switch문에 각 case문마다 반드시 break문을 사용해야하는 것은 아니며, 이 성질을 잘 이용하면 보다 간결하고 논리적으로 명확한 코드를 작성할 수 있게 된다. 

한 가지 더 간단한 예를 들어 보겠다. 아래의 코드는 전체 코드가 아닌 코드의 일부를 발췌한 것인데, 
회원제로 운영되는 인터넷 사이트에서 많이 사용될 만한 코드이다. 


switch (level) { 
      case 3 : 
            grantDelete();       // 삭제권한을 준다. 
      case 2 : 
            grantWrite();       // 쓰기권한을 준다. 
      case 1 : 
            grantRead();       // 읽기권한을 준다. 



로그인한 사용자의 등급(level)을 체크하여, 등급에 맞는 권한을 부여하는 방식으로 되어 있다. 제일 높은 등급인 3을 가진 사용자는 grantDelete, grantWrite, grantRead메서드가 모두 수행되어 읽기, 쓰기, 삭제 기능까지 모두 갖게 되고, 제일 낮은 등급인 1을 가진 사용자는 읽기 권한만을 갖게 된다. 
[참고]위의 코드는 사용자에게 읽기, 쓰기, 삭제권한을 주는 기능의 grantRead(), grantWrite(), grantDelete()가 존재한다는 가정 하에 작성되었다. 

[예제4-10] FlowEx10.java

class FlowEx10 

      public static void main(String[] args) 
      { 
            int score = 88; 
            char grade ='\u0000'; 
            switch(score) { 
                  case 100: case 99: case 98: case 97: case 96: 
                  case 95: case 94: case 93: case 92: case 91: 
                  case 90 : 
                        grade = 'A'; 
                        break; 
                  case 89: case 88: case 87: case 86: 
                  case 85: case 84: case 83: case 82: case 81: 
                  case 80 : 
                        grade = 'B'; 
                        break; 
                  case 79: case 78: case 77: case 76: 
                  case 75: case 74: case 73: case 72: case 71: 
                  case 70 : 
                        grade = 'C'; 
                        break; 
                  case 69: case 68: case 67: case 66: 
                  case 65: case 64: case 63: case 62: case 61: 
                  case 60 : 
                        grade = 'D'; 
                        break; 
                  default : 
                        grade = 'F'; 
            } // end of switch 
            System.out.println("당신의 학점은 " + grade + "입니다."); 

      }       // end of main 
}             // end of class 
[실행결과]
당신의 학점은 B입니다. 

위의 예제는 예제4-3을 switch문을 이용해서 변형한 예제이다. 위의 예제를 if문을 이용해서 구현하려면, 조건식이 4개가 필요하며, 최대 4번의 조건식을 계산해야한다. 하지만, switch문은 조건식을 1번만 계산하면 되므로 더 빠르다. 하지만, case문이 너무 많아지므로 좋지 않다.

반드시 속도를 더 향상시켜야 한다면 복잡하더라도 switch문을 선택해야겠지만, 그렇지 않다면 이런 경우 if문이 더 적합하다. 

[예제4-11] FlowEx11.java

class FlowEx11 

      public static void main(String[] args) 
      { 
            int score = 88; 
            char grade ='\u0000'; 
            switch(score/10) { 
                  case 10: 
                  case 9 : 
                        grade = 'A'; 
                        break; 
                  case 8 : 
                        grade = 'B'; 
                        break; 
                  case 7 : 
                        grade = 'C'; 
                        break; 
                  case 6 : 
                        grade = 'D'; 
                        break; 
                  default : 
                        grade = 'F'; 
            } 
            System.out.println("당신의 학점은 " + grade + "입니다."); 
      } 

[실행결과]
당신의 학점은 B입니다. 

이전 예제에 기교를 부려서 보다 간결하게 작성한 예제이다. score를 10으로 나누면, 전에 배운 것과 같이 int / int의 결과는 int이기 때문에, 예를 들어 88/10은 8.8이 아니라 8을 얻는다. 따라서 80과 89사이의 숫자들은 10으로 나누면 결과가 8이 된다. 마찬가지로 70~79사이의 숫자들은 10으로 나누면 7이 된다.

출처 - http://cafe.naver.com/javachobostudy