Ch04. 조건문과 반복문

ho_c·2023년 3월 28일
0

자바의 정석 3판

목록 보기
3/8
post-thumbnail

들어가는 말

  • 컴퓨터는 소스코드를 위에서 아래로 순서대로 실행하고 이를 ‘흐름’이라고 한다.
    하지만 실제로 코드를 짜고 애플리케이션을 만들다 보면, 한 방향으로 코드가 진행될 경우 흐름이 복잡해져 유지보수도 어려워진다.

  • 또 컴퓨터의 매력이 반복작업을 매우 빨리 처리하는 것인데, 그럼 같은 코드를 100번씩이나 중복해서 넣는 것도 비효율적이다.
    그래서 이런 한 방향의 흐름을 원하는 곳으로, 또는 계속 순환시키도록 하는 문장을 ‘제어문’이라 한다.


1. 조건문: if, switch

  • 조건문은 조건식 + {}(블록)으로 구성되고, 블록 안에는 조건식의 연산결과에 따라 실행될 문장들이 들어간다.

조건문은 if, switch문 두 가지인데, 처리할 조건이 많을수록 switch문이 깔쌈하지만 대신 if문보다 제약이 많다.


if문

  • 가장 기본적인 조건문으로, (조건식)의 결과가 ‘참’(true)이면 {}안의 코드를 실행한다.
if(조건식){
	// true일 경우 실행.	
}

조건식

  • (조건식)은 비교연산자(<= >= ==)와 논리연산자(&& ||)의 조합으로 구성된다.

블록 {}

  • {}을 이용하면 많은 코드를 하나의 영역(scope)로 묶어준다.
    블록이 시작되면 들여쓰기로 해당 위치를 표시해주는 것이 좋다.

여기서 조건문의 블록 내 실행 코드가 한 줄이면, 블록을 생략하고 한 줄로 쓸 수 있다.

if(score > 60)
System.out.println("합격입니다."); // 개인적으로 비추.

if-else문

  • if-else는 if문의 변형으로, else블록이 마지막에 추가된다.
  • else블록은 포함된 모든 조건식의 결과가 거짓일 때, 실행된다.
  • else 역시 한 줄일 경우 생략이 가능하다.

다만, 중첩 if문에서 생략해서 쓸 경우 가장 가까운 if문의 블록이 된다.

if(조건식){
	System.out.println("진실입니다."): // 조건식이 참일 때.
} else { 
	System.out.println("거짓입니다."); //조건식이 거짓일 때.
}

상반된 조건일 때 사용하면 좋다.

  • else문이 존재하면, 해당 if문은 무조건 한번은 실행되게 된다.
    특히 조건이 상반되는 경우, else문을 사용하면 코드를 효율적으로 작성할 수 있다.
// 상반된 조건
if(input == 0){
	System.out.println("0입니다.");
}
if(input != 0){
	System.out.println("00이 아닙니다.");
}

// if-else로 변경
if(input == 0){
	System.out.println("0입니다.");
}
else{
	System.out.println("0이 아닙니다.");
}

if-else if문

  • if문이 하나면 조건이 하나, if-else는 둘 중 하나가 실행된다. 즉, 최대조건이 2개밖에 작성을 못한다.

그래서 하나의 조건문에 여러 조건을 넣을 경우 else if블록을 추가하면 된다.

if(조건 1){
	// 실행 1
} else if(조건 2){
	// 실행 2
} else if(조건 3){
	// 실행 3
} else {
	// 실행 4 : 1,2,3이 거짓일 때 실행
}

여기서 재밌는 건, 이전의 평가한 조건을 기반으로 평가가 된다는 것이다.

if (score >= 90) (
	grade = 'A';
} else if (80 <= score && score < 90) { // 80 ≤ score 90
	grade = 'B';
} else if (70 <= score score < 80) { // 70 ≤score 80
	grade = 'C';
} else { // score < 70
	grade = 'D';
}

// 이전 조건을 참조
if (score >= 90) (
	grade = 'A';
} else if (80 <= score) { // 80 ≤ score 90
	grade = 'B';
} else if (70 <= score ) {// 70 ≤score 80
	grade = 'C';
} else { // score < 70
	grade = 'D';
}

위 코드에서 보듯이, if문은 첫 번째부터 순서대로 조건을 검사한다. 따라서 이미 기존에 검사했던 결과를 참조함으로써 중복 조건을 최대한 배제한다.


○ 요약

  • 첫 번째 조건부터 순서대로 비교된다.
  • 참인 조건을 만나면 해당 블록의 동작을 수행 후 if문 전체를 벗어난다.
  • else if의 조건은 이전의 평가된 조건식의 결과를 참고 한다.
  • if문을 여러개로 나눈다고 else if문은 아니다. 그래서 앞서 말한 혜택은 else if일때만 가능하다.

중첩 if문

  • if 조건문 안에 새로운 조건을 계속 중첩해서 만드는 것을 말한다.
    중첩 횟수는 제한이 없지만 많을수록 가독성은 떨어진다.
if(조건 1){
	// 조건 1 : true일 때만 if문 비교
	if(조건 2){
		// 조건 1, 2 모두 true
	} else {
		// 조건 1 : true, 조건 2 : false
	}

} else {
	// 조건 1 : false
}

switch문

  • if문은 값은 참과 거짓으로 판단해서 코드를 실행한다.
    그래서 조건이 추가될수록 else-if 만 많아진다.
    이와 달리 switch문은 하나의 조건식에 여러 개의 값을 걸어둘 수 있다.
    다만 if문과 달리 제약 조건이 있다.

switch문의 동작 순서

① 조건식 연산 → ② 결과와 일치하는 case문으로 이동 → ③ 문장 수행 → ④ break문 or switch문의 끝을 만나면 종료

switch (조건식)  {
	case1 :
		// 수행문 1
		// 수행문 2
		break; // switch문 이탈
	case2 :
		// 수행문 3
		// 수행문 4
		break; // switch문 이탈

	default : // else문의 역할
		
}

여기서 주의할 점은 break문의 위치이다. 만약 break문을 작성해주지 않는다면, 최상단 case문에서 시작할 경우 switch문의 끝에 닿을 때까지 포함된 모든 수행문을 실행할 것이다.


switch문의 제약 조건

  • 조건식의 결과값이 무조건 정수여야 한다. (문자열도 가능)
  • 따라서 case 역시 정수와 상수만 가능하다. (중복 X)

switch문의 중첩

  • if문처럼 switch문 안에 switch문도 가능하다. 근데 막상 보면 if문이 더 간다해 보인다.
switch (조건식1)(
	case '1': case '3':
		switch (조건식2){
			case 'a':
				// 수행문 a
				break;
			case 'b':
				// 수행문 b
		}
		break; // case 1,3의 break
	case '2': case '4':
		switch (조건식3){
			case 'c':
				// 수행문 c
				break;
			case 'd': 
				// 수행문 d
		}
}

○ 요약

  • 하나의 조건식으로 여러 값을 사용할 때, switch문을 사용한다.
  • 조건의 결과는 정수 or 문자열만 가능하다.
  • case문의 값은 정수, 상수만 가능하고 중복은 불가능하다.
  • default문은 else문과 같은 역할이다. 마지막에 위치해서 break는 안 써도 된다.
  • break를 case문 사이마다 작성하지 않으면 그 사이에 있는 수행문이 다 실행된다.

2. 반복문: for, while, do-while

사람은 반복하는 작업을 지루해하지만, 컴퓨터는 반복 작업을 하기 위해 존재한다. 그래서 컴퓨터에게 반복작업을 시키기 위해 ‘반복문’을 배워보자.

for문

  • for문은 주로 반복 작업의 횟수가 적당할 때 사용한다.
    구조는 초기화, 조건식, 증감식, 블록{}으로 이루어져 있다.
for(초기화; 조건식; 증감식){
	// 수행문;
}

for(int i = 1; i<=10; i++){
	System.out.println(“공부하기 싫다.);
}

해당 for문을 실행하면 선언된 변수 i에 1이 저장된 후, 조건식이 평가된다. 결과가 true이면 수행문을 실행하고, 증감식이 실행되면 1→2로 변수 i에 저장된 값이 올라간다.


for문의 구조와 수행 순서

  • 앞 전의 코드에서 본 것처럼 for문은 초기화, 조건식, 증감식, 블록으로 이루어져 있다.
  • 조건식이 참일 때, 블록 안의 코드를 수행, 거짓이 되면 이탈한다.

① 초기화

  • 반복문에서 사용할 변수를 선언하고, 초기화한다.
  • for문에서 제일 먼저 실행되며, 처음 한번만 수행된다.
  • 변수가 여러 개이면 , 으로 구분한다.
for (int i=1;i<= 10;i++) { ... } 
for (int i=1,j=0;i<=10;i++) { ... } 

② 조건식

  • 조건의 값이 참이면 반복하고, 거짓이면 반복을 멈추고 for문을 이탈한다.
  • 배열을 다룰 때는 배열의 길이를 활용한다.

③ 증감식

  • 선언된 변수의 값을 증감하는 식이다. 이로 인해서 반복의 끝낼 수 있다.
  • 증감연산자, 복합대입연산자를 주로 이용한다.
  • ,를 이용해서 여러 변수를 증감할 수 있다.

○ 요약

  • 반복횟수를 알 때, 쓰기 좋다.
  • 변수와 증감식은 여러개가 될 수 있다.
  • 조건식이 거짓이 되면 반복이 종료된다.
  • 수행문이 한 줄이면, {}을 생략할 수 있다.
  • 초기화, 조건식, 증감식을 모두 생략하면 무한반복문이 된다.

중첩 for문

  • 조건문처럼 반복문도 중첩이 가능하다. 일종의 멀티 플레이가 가능하다고 생각하는게 편하다.
    중첩의 횟수제한은 없지만, 이 역시 여러번 중첩되면 가독성이 떨어진다.
// 구구단 예제
for(int x = 1; x < 10; x++;){
	for(int y = 1; y < 10; y++){
		System.out.printf(%d * %d = %d%n”, x,y,x*y);
	}
}

향상된 for문

  • 애플리케이션에 객체 배열 안에 있는 DTO의 정보들을 다룰 때, 주로 사용하는 for문이다.
  • 쉽게 말해서 값은 타입의 객체를 대량으로 다룰 때, 변수 한 개만으로 반복작업을 할 수 있게 해주는 for문이다.
for (타입 변수명 : 배열 또는 컬렉션){
	// 수행문
}

int[] arr = [];
for (int a : arr){
	// 수행문
}

배열이나 컬렉션 안에 저장된 값들을 그 값에 맞는 타입의 변수에 연결해서 다루는 것이다. 따라서 꺼내는 값의 타입과 변수의 타입이 일치해야 된다.


while문

  • 반복문의 원조는 while문이다. 그래서 for문은 언제든지 while문으로 while 역시 for문으로 변경해서 사용할 수 있다.
while(조건식){
	// 수행문
}

for문과 while문의 비교

for문과 while의 차이는 뭘까?

  • 일단 둘 다 같은 작업을 코드로 만들 수 있다. 다만 while문은 for문과 달리 조건식이 “거짓”이 될 때까지 반복한다.
// for
for(int i=1;i<=10;i++) {
	System.out.println(i);
}

// while
int i = 1;
while(i<=10){
	System.out.println(i);
	i++;     	
}  
  • 또 예를 들어 특정 조건일 때, 무한 반복을 하게 되는 코드의 경우도 while문으로 변환이 가능하다.
 if(i%5==0){
        for(;;)
}
while(i%5==0){

}

while문의 조건식은 생략이 안된다.

  • for문은 조건식 생략으로 무한루프를 만들기도 하지만, while은 조건식이 없으면 안된다.
    그래서 무한루프를 만들려면 조건식이 항상 참이어야 한다.
while(true){
	// 수행문
}

다만 무한 반복문의 경우, 끝내지만 않으면 계속 돌아가기 때문에 특정 조건을 이용해서 멈추는 break문을 만들어줘야 한다.


do-while문

  • do-while은 while문의 변형으로 차이점은 최소한 한번은 수행문이 반복된다는 걸 보장한다는 것이다.
do {
	// 조건식의 연산결과가 참일 때 수행.
} while (조건식); // 여기에 ;이 붙음

while은 조건식이 거짓이면, 수행되지 않는다.
반대로 do-while은 do절의 수행문이 먼저 실행되고 조건식이 평가되므로 최소한 한번은 수행된다.


○ 요약

  • while문은 조건이 참인 동안 반복된다.
  • while의 조건식은 생략을 할 수 없다.
  • do-while은 수행문이 먼저 실행되고 조건식이 평가된다.

break문

  • break라는 예약어를 만나면 자신이 포함된 조건문, 반복문을 이탈한다.
    한마디로 코드의 진행이 멈추게 된다.
int x = 0;

while(true){
	x++:
	if (x>2000){ // x의 값이 2000을 넘기면 실행
		break; // while문을 벗어난다.
	}
}

continue문

  • break가 반복을 멈추는 것이라면, continue는 반복을 뛰어넘는다.
    즉 skip의 역할을 하는데, 해당 반복문의 끝으로 이동해서 다음 반복으로 넘어간다.
  • break와 달리 반복문 자체를 벗어나지 않는다.
for (int i=0; i<=10; i++){
	if (i%3==0){
		continue; // 3의 배수일 때마다 반복을 넘긴다.
	}
	System.out.println(i); // 1, 2, 4, 5, 7, 8, 10
} ← 여기로 이동

이름 붙은 반복문

  • break의 특징은 가장 가까운 하나의 반복문만 벗어날 수 있다.
    그래서 중첩 반복문 같은 경우에는 한 번에 끝내려면 break를 중첩해서 사용해야된다,
  • 그치만 그럼 귀찮으니까, 반복문에 이름을 정해줘서 해당 반복문을 끝내거나, 뛰어넘는 방식이 존재한다.
Loop1 : for(int i=2; i<=9; i++) {
	for (int j=1;j<=9;j++) {
		if(j==5){
			break Loop1;
			// break; : j 반복문만 끝냄.
		}
		System.out.println(i+"*"+j+"="+ i*j);
	} 
} // Loop1의 끝

만약 반복문의 이름을 붙이지 않았다면, break는 j가 5일 때마다 멈춰서, 구구단 2단부터 9단까지 4까지만 출력이 될 것이다.
하지만 이름을 붙였기 때문에 2*1, 2*2, 2*3, 2*4만 출력된다.


○ 요약

  • break문은 가장 가까운 단 하나의 반복문을 이탈한다.
  • continue는 해당 반복문의 끝으로 이동해서 다음 반복으로 넘어간다.
  • 반복문에 이름을 붙여서 break, continue를 사용하면 명명된 반복을 이탈하거나, 넘어간다.
  • 반복문에 이름을 붙이는 건, while의 무한반복문에서 주로 사용한다. (전체 종료)


도움이 되셨다면 '좋아요' 부탁드립니다 :)

profile
기록을 쌓아갑니다.

0개의 댓글