Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- MVC 패턴
- VS Code
- DB 모델링
- 마크다운
- 정보처리기사
- SQL
- 도커
- docker 소개
- 프로그래밍 기초
- 데이터베이스
- mybatis
- java
- DB 개요
- docker
- Flutter
- java 기초
- dql
- ORACLE 기초
- github
- oracle
- DB
- 쿠버네티스
- DDL
- 기초 선택자
- 기본 API
- view
- 깃허브
- 필기
- 쿠버네티스 기본 개념
- 웹개발 기초
Archives
- Today
- Total
핑구
03. 제어문 본문
📅 2021.08.11 ~ 2021.08.17
제어문
- 원래 코드의 흐름은 위에서 아래로 차례대로 진행되는데, 제어문은 코드를 건너뛴다거나 반복을 하는 등 코드의 흐름을 바꿔 주는 역할을 한다.
조건문
- 조건에 따라 다른 문장이 수행되도록 만들어 준다.
- if
- if문 : if(조건식) { }의 형태이며, 조건식이 true인 경우에 { } 내부의 내용을 실행한다. 조건식이 false인 경우에는 해당 코드를 건너뛴다.
하나의 조건만 실행하기 때문에 여러 조건을 추가하고 싶을 경우 여러 개의 if를 만들어야 한다. 하지만 조건이 위의 if문과 겹치는 경우에는 두 부분 모두 실행되기 때문에 하나만 실행하고 싶은 경우에는 if문을 중첩시켜 사용하여야 한다. - if~else문 : if(조건식) { } else { }의 형태이며, 조건식이 true인 경우에는 if 부분 중괄호의 내용을 실행하고, false인 경우에는 else 부분 중괄호의 내용을 실행한다.
조건식은 if에만 붙을 수 있으며, else에는 붙여서는 안 된다..
if~else는 항상 세트이기 때문에 여러 개를 추가할 수 없다. 다음과 같은 코드는 실행할 수 없다.
if(a == 0){ } if(a > 0) { } else { } else { }
- if~else if~else문 : if(조건식1) { } else if(조건식2) { } else { }의 형태이며 다중 조건을 넣을 수 있다. 조건식1이 true이면 if 부분 중괄호의 내용을 실행하고, 조건식1이 false이면 조건식2를 판단한다. 조건식2가 true이면 else if 부분 중괄호의 내용을 실행하고, 조건식2가 false이면 else 부분 중괄호의 내용을 실행한다.
if~else if~else문은 상위 조건이 true일 경우에는 하위 조건을 검사하지 않는다. 따라서 상위 조건과 하위 조건이 겹쳐도 중복 출력되지 않는다. 따라서 여러 조건을 실행하기 위해 중첩할 필요가 없다.
else if문은 여러 개가 존재할 수 있으며, else문은 존재하지 않아도 된다.
- if문 : if(조건식) { }의 형태이며, 조건식이 true인 경우에 { } 내부의 내용을 실행한다. 조건식이 false인 경우에는 해당 코드를 건너뛴다.
- char로 저장한 숫자 형태의 문자를 비교할 때는 바로 비교 연산자를 사용해 비교할 수 없다. (ex. ch == 1) 숫자로 비교하는 경우 아스키코드에 매칭된 숫자와 비교되기 때문에 char형은 반드시 홑따옴표를 사용하여 비교해주어야 한다. (ex. ch == '1')
- 문자열끼리 비교하는 경우에는 비교 연산자(==)를 사용하여 비교할 수 없다. equal() 메소드를 이용하여야 문자열끼리의 올바른 비교가 가능하다.
if(name == "사과") // 비교 불가능 if(name.equals("사과")) // 비교 가능 (name이 "사과"랑 같은가?) if("사과".equals(name)) // 반대로도 비교 가능 ("사과"가 name과 같은가?)
- switch :
switch(조건식) {
case 조건식의 결과:
} 형태이며, case 뒤에는 반드시 콜론(:)을 붙여야 한다.- 조건식 하나로 많은 경우의 수를 처리할 경우에 사용된다. if의 조건식은 항상 논리값이 나와야 하지만, switch의 조건식은 결과가 정수, 문자, 문자열이어야 한다. (조건식과 case를 비교하여 맞는 caase문에 들어가기 위함이다.)
- 조건식의 결과가 실수가 되어서는 안 된다. 실수는 컴퓨터가 인식 가능한 이진수로 변경할 경우 정수처럼 떨어지지 않는 숫자가 존재하기 때문에 오차가 생길 수 있고, 이 오차로 인해 올바른 case문을 수행하지 못할 수 있기 때문이다.
- 조건식과 일치하는 case문을 실행하며, 일치하는 case가 존재하지 않는 경우에는 아무것도 수행하지 않는다. 하지만 default가 있는 경우에는 해당되는 case가 없을 때 default를 수행한다.
- break는 코드의 수행을 멈추는 역할을 하며, 다음 case로 넘어가는 것을 방지한다. 만약 break가 없는 다음 코드의 경우 모든 문장이 실행된다.
num은 1이므로 case 1을 처음 수행하나, 빠져나오는 break가 없기 때문에 아래 case도 모두 실행되므로 결과는 아래처럼 출력된다.int num = 1; switch(num) { case 1: System.out,println("숫자는 1입니다."); case 2: System.out,println("숫자는 2입니다."); case 3: System.out,println("숫자는 3입니다."); }
결과: 숫자는 1입니다.
숫자는 2입니다.
숫자는 3입니다.
하지만 여러 케이스를 한 번에 묶고 싶은 경우/하나로 처리하고 싶은 경우에는 다음과 같이 의도적으로 break를 작성하지 않을 수 있다.switch(num) { case 1: case 3: case 5: System.out.println("해당 숫자는 홀수입니다."); break; case 2: case 4: case 6: System.out.println("해당 숫자는 짝수입니다."); }
- default 혹은 마지막 case에는 break를 추가하지 않아도 된다. (이후 다른 문장이 실행되지 않고 switch문이 종료되기 때문이다.)
defalut 또한 else처럼 반드시 존재해야 하는 것은 아니다.
default를 이용해 존재하지 않는 case에 대한 예외를 만들어 주는 경우 boolean 변수와 if문을 추가하여 case가 실행된 경우에만 수행되는 코드를 작성할 수 있다.boolean flag = false; // 존재하지 않는 과일일 때 가격 출력하지 않기 switch(fruit) { case "사과": price = 1000; break; case "키위": price = 5000; break; default : System.out.println("그 과일은 없습니다."); flag = true; break; } if(!flag) { // case문이 실행되는 경우에만 해당 문구 출력 System.out.println(fruit + "의 가격은 " + price + "원입니다."); }
- 메소드 호출
다른 클래스의 메소드를 호출하는 경우에는 A_If ai = new A_If();의 형태로 알맹이를 만들어 주어야 호출이 가능하다. 하지만 만약 같은 클래스의 메소드를 호출한다면 알맹이 없이 호출하여야 한다.
메소드를 호출한 후 해당 매소드가 실행된 후 종료되면 나를 호출한 쪽으로 돌아간다.
메소드의 시작 및 종료 순서는 다음과 같다.// 1. 프로그램 시작 시 JVM이 main 메소드를 호출한다. public static void main(String[] args) { B_Switch bs = new B_Switch(); bs.method2(); // 2. 메인 메소드에서 B_Switch 클래스의 method2를 호출했으므로 // 해당 메소드의 시작 부분으로 간다. // 11. bs.method2가 종료되고 자기를 호출한 쪽으로 돌아온다. } // 12. main 메소드 종료 // main 메소드는 종료된 후 JVM으로 돌아가며, JVM이 프로그램을 종료한다.
public class B_Switch { // 5. method1 실행 public void method1() { int num = 1; switch(num) { case 1: // 6. 순서대로 실행되고, switch문에 들어와서 num은 1이기 때문에 case 1을 실행한다. System.out,println("숫자는 1입니다."); break; case 2: System.out,println("숫자는 2입니다."); break; case 3: System.out,println("숫자는 3입니다."); break; } // 7. break를 만나 switch문에서 빠져나온다. } // 8. method1 종료 // 3. method2 실행 public void method2() { method1(); // 4. method2에서 같은 클래스의 method1을 호출했으므로 // 해당 메소드의 시작 부분으로 간다. // 9. method1이 종료된 후 method1을 호출한 쪽으로 넘어온다. } // 10. method2 종료 }
- 모든 if는 switch로 변경 가능하나, 모든 switch는 if로 변경 불가능하다.
→ 이유 : if문은 조건식의 결과만 boolean으로 나온다면 조건식 안에는 모든 자료형을 사용할 수 있다. 정수끼리의 연산, 실수끼리의 연산 등 모든 연산의 결과가 boolean이기만 하면 조건식에 사용할 수 있는 것이다. 하지만 switch문은 조건식에 char, int, String 타입만 사용 가능하다. 따라서 사용할 수 있는 자료형이 많은 if는 switch로 변경할 수 없으며, switch는 if로 변경이 가능하다.
반복문
- 특정 문장들을 반복하여 수행하게 만드는 제어문이다.
- for : for(초기식; 조건식; 증감식) {}의 형태이다.
- 초기식, 조건식, 증감식은 생략 가능하지만, 생략될 경우 무한 반복이 일어날 가능성이 있다.
초기식 생략 : for(; 조건식; 증감식)
조건식 생략 : for(초기식;;증감식)
증감식 생략 : for(초기식; 조건식;)
전체 생략 : for(;;) - for문의 증감식에는 주로 후위 연산자를 사용하는데, (ex. i++) 꼭 후위 연산자를 사용해야 하는 것은 아니며, 전위 연산자를 사용하여도 결과는 동일하게 출력된다. 하지만 후위 연산자를 사용하는 경우가 더 많으므로 보통의 경우 후위 연산자를 사용한다. 전위 연산자를 사용하는 경우 연산의 속도가 더 빨라지는 장점은 있다. 이유는 다음과 같다.
// 전위 연산자 사용 for( int i = 0; i < 10; ++i ) { printf( "num: %d", i ); } // operator 코드 int int::operator++() { this = this + 1; return this; } // 후위 연산자 사용 for( int i = 0; i < 10; i++ ) { printf( "num: %d", i ); } // operator 코드 int int::operator++() { int returnval = this; i = i + 1; return returnval; }
후위 연산자의 경우 operator 코드에 한 줄이 더 추가된다. 임시 변수를 생성하는 과정이다. 하지만 전위 연산자는 자기 자신에 더하기 때문에 추가되지 않고, 속도가 조금 더 빠르다. 하드웨어 성능이 증가하고 컴파일러가 최적화된 현재는 유의미한 차이는 아니다. - for문의 수행 순서는 다음과 같다.
- 초기식 진행
- 조건식이 true인지 확인
- true인 경우 for문 안에 있는 문장을 수행
3-1. 모든 문장을 수행한 경우 증감식 수행
3-2. 증감식으로 인해 증가/감소된 값이 아직 조건식에 맞는지 확인 - false인 경우 for문 탈출
※ 조건식이 false가 될 때까지 3을 반복한다.
- for문 반복
for(int i = 1; i < 5; i++) { System.out.println(i + "출력"); }
- int i = 1, true, 1 출력
- (i++) 2, true, 2 출력
- (i++) 3, true, 3 출력
- (i++) 4, true, 4 출력
- (i++) 5, false, for문 종료
- for문 또한 중첩 사용이 가능하며, 크게 한 번 반복할 때 작게 여러 번 반복해야 하는 경우에 중첩 for문을 사용한다. (ex.구구단 - 바깥쪽 for문으로 단을 만들고, 안쪽 for문으로 1~9를 곱해줌)
중복 for문을 사용할 경우 하위 for문 초기식에 상위 for문 초기식에 사용했던 변수 이름을 다시 사용하여 선언해서는 안 된다. 이미 스택에 선언되어 있는 변수이기 때문에 오류가 발생한다.
- 초기식, 조건식, 증감식은 생략 가능하지만, 생략될 경우 무한 반복이 일어날 가능성이 있다.
- while
- while문 :
while(조건식){
수행될 문장;
[증감식 or 분기문];
}
형태로 [] 안의 내용은 생략될 수 있다. - 반복하는 조건(끝나는 조건)만 있고 몇 번 반복할지 확실하지 않은 경우 주로 사용된다. 반복 횟수가 정해진 경우에도 사용 가능하나 해당 경우에는 가시성의 이유로 for를 더 주로 사용한다.
- for와 while은 상호 호환이 가능하지만 자주 사용되는 부분에서 차이가 있다.
- while도 중첩하여 사용 가능하다. 중첩해서 사용하는 경우 하위 while의 조건문에 사용되는 변수가 상위 while문이 한 번 돌 때마다 초기화되어야 하는 경우 상위 while문 안에서 선언하여야 한다.
- while은 무한 반복문으로도 많이 사용된다. 무한 루프를 만드는 방법은 조건식에 true을 넣어 while(true) 형태로 사용하는 것이다. 하지만, 이 경우 반드시 탈출 조건이 있어야 한다.
- 메소드의 끝에 도달하면 나를 호출해 준 메소드로 돌아가는데, 이것은 사실 끝에 도달했기 때문에 돌아가는 것이 아니라, 메소드 끝에 있는 return;(생략 가능)을 만났기 때문이다. 따라서 return은 나를 호출해 준 메소드로 돌아가는 역할이라고 할 수 있으며, 이것을 이용해 무한루프를 탈출할 수 있다.
- 아래의 경우 9를 입력하면 return이 실행되며 나를 호출한 메소드로 돌아가고, 해당 메소드는 종료된다.
while(true) { // 무조건 실행되게 만들고 싶을 경우 -> 무한 반복문 System.out.println("1. 1~5까지 출력"); System.out.println("2. 5~1 까지 출력"); System.out.println("3. 문자열 인덱스"); System.out.println("9. 종료"); System.out.print("메뉴 선택 : "); int menuNum = sc.nextInt(); switch(menuNum) { case 1: method1(); break; case 2: method2(); break; case 3: method3(); break; case 9: System.out.println("종료합니다."); return; // 종료 조건을 넣지 않고 무한 반복인 경우에 종료 조건으로 return을 삽입 default: System.out.println("잘못 입력하셨습니다."); } }
- while문 반복
int i = 1; while(i < 5) { System.out.println(i + " 출력"); i++ }
- int i = 1, true, 1 출력
- (i++) 2, true, 2 출력
- (i++) 3, true, 3 출력
- (i++) 4, true, 4 출력
- (i++) 5, false, while문 종료
- while문 :
- do~while문 :
do{
수행될 문장;
[증감식 or 분기문];
} while(조건식);
형태로 [] 안의 내용은 생략될 수 있다- 반드시 while(조건식) 뒤에 세미콜론(;)을 붙여야 한다.
- do 안의 내용을 먼저 실행하고 조건식을 검사하므로 안의 내용은 무조건 한 번 이상 실행된다.
- 조건식에 사용할 변수는 do~while문 밖에 선언하여야 한다. do{} 내부의 내용은 do{} 내부에만 영향을 끼치고, 조건식에는 영향을 끼치지 않는다. 따라서 조건식을 검사할 때는 선언되지 않은 변수가 되기 때문에 오류가 발생한다.
- while이 아닌 do~while을 사용하여야 하는 경우
String str = null; while(!str.equals("exit")) { System.out.print("문자열 입력 : "); str = sc.nextLine(); System.out.println("str : " + str); }
위처럼 str에 null이 할당되었는데, while의 조건문으로 str.equals()가 사용되면 null pointer exception 오류가 발생한다. 해당 오류는 null을 참조하는 경우에 발생한다. 따라서 다른 변수들은 초기화 시 기본값으로 초기화를 진행하나, 클래스의 메소드를 사용하여야 하는 참조 변수의 경우 기본값인 null을 할당해서는 안 되며, ""와 같이 빈 값을 할당하여야 한다.
하지만 아래와 같은 do~while문의 경우에는 do{} 부분이 무조건 한 번 이상 실행되기 때문에 null로 할당된 이후 다른 값이 다시 할당되기 때문에 null을 참조하는 것이 아니며, 오류가 발생하지 않는다.String str = null; do { System.out.print("문자열 입력 : "); str = sc.nextLine(); System.out.println("str : " + str); } while (!str.equals("exit"));
- 클래스 자료형의 기본값은 null이다. 이 null은 아무것도 존재하지 않는 것이기 때문에 null을 할당하는 경우 해당 클래스 내부의 메소드 또한 사용할 수 없다.
모든 클래스의 메소드를 사용할 때는 알맹이를 만들어 준 후에 사용이 가능한데, (ex. bw.method9()) null의 경우 이 알맹이가 없는 것이기 때문에 사용이 불가능하다.
따라서 문자열에 null을 할당하는 경우 해당 문자열을 equals() 메소드를 이용해 비교할 수 없다. - 문자열의 길이를 반환하는 방법: String 클래스의 length 메소드 사용 문자열의 길이를 반환하기 때문에 int형을 반환하며, 문자열.length() 형태로 사용한다.
- return은 나를 호출한 메소드로 돌아가는 기능을 제공하며, 돌아갈 때 값을 가지고 돌아갈 수도 있다. return을 사용하는 경우 return 아래에 있는 코드는 모두 무시하고 돌아가기 때문에 해당 코드에 도달할 수 없는 코드라는 에러가 발생한다. 따라서 사용 시 주의하여야 한다.
- 클래스 자료형의 기본값은 null이다. 이 null은 아무것도 존재하지 않는 것이기 때문에 null을 할당하는 경우 해당 클래스 내부의 메소드 또한 사용할 수 없다.
분기문
- break문 : 반복문 내에 break가 있는 경우 break를 만나면 자신이 포함된 가장 가까운 반복문을 빠져나간다.
switch문에서의 break는 switch문만 빠져나갔지만, 반복문 내부에 있는 경우는 가장 가까운 반복문을 빠져나간다. 따라서 for문 내의 if문에 break가 있는 경우에도 break를 만나면 for문을 빠져나간다. - continue문 : 반복문 내에서만 사용이 가능하며, continue 아래 부분은 실행하지 않고 반복문을 다시 실행한다.