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
- 자바의 정석 기초편 ch6
- 코드로 시작하는 자바 첫걸음
- 스프링 고급 - 스프링 aop
- 스프링 mvc2 - 로그인 처리
- 자바의 정석 기초편 ch1
- 스프링 mvc2 - 타임리프
- 자바의 정석 기초편 ch2
- 자바의 정석 기초편 ch13
- jpa 활용2 - api 개발 고급
- 자바 기본편 - 다형성
- 스프링 mvc1 - 서블릿
- 스프링 db2 - 데이터 접근 기술
- 스프링 mvc1 - 스프링 mvc
- 게시글 목록 api
- 자바의 정석 기초편 ch14
- @Aspect
- 자바의 정석 기초편 ch9
- 2024 정보처리기사 시나공 필기
- 자바의 정석 기초편 ch4
- 스프링 db1 - 스프링과 문제 해결
- jpa - 객체지향 쿼리 언어
- 자바의 정석 기초편 ch7
- 자바의 정석 기초편 ch8
- 스프링 입문(무료)
- 자바의 정석 기초편 ch5
- 2024 정보처리기사 수제비 실기
- 자바의 정석 기초편 ch12
- 자바의 정석 기초편 ch3
- 스프링 mvc2 - 검증
- 자바의 정석 기초편 ch11
Archives
- Today
- Total
나구리의 개발공부기록
코드로 시작하는 자바 첫걸음3, 스코프 및 형변환, 훈련 본문
출처 : 인프런 - 자바 입문 - 코드로시작하는 자바 첫걸음 (무료) / 김영한님
1. 스코프 및 형변환
1) 지역 변수와 스코프
(1) 지역 변수
- 변수는 선언한 위치에 따라 지역 변수, 멤버 변수(클래스 변수, 인스턴스 변수)와 같이 분류됨
- 지금까지 학습한 변수들은 모두 영어로 로컬 변수(Local Variable), 한글로 지역 변수라 하며 나머지 변수들은 뒤에서 학습함
- 지역 변수는 말그대로 특정 지역에서만 사용할 수 있는 변수를 뜻이며 특정 지역을 벗어나면 사용할 수 없음
- 여기서 말하는 지역은 변수가 선언된 코드 블록{ } 이며 지역변수는 자신이 선언된 코드 블록 안에서만 생존하고 자신이 선언된 코드 블록을 벗어나면 제거되며 이후에는 접근할 수 없음
(2) 예제
- 변수 m은 main의 코드 블록 안에서 선언된 지역 변수이며 main(String[] args) { <- 여기의 블록에서부터 마지막 블록 전의 블록 까지 생존함(맨 마지막의 블록은 클래스의 범위)
- if 문의 블록안에서 선언된 변수 x의 생존범위는 if문의 블록까지만 생존하며 if문 밖에서 선언된 변수도 if문 내부 에서 사용할 수 있음
- 즉, 외부에서 선언된 변수도 내부에서는 모두 접근 가능하며 각자의 변수는 자신이 속한 블록안에서만 생존하는데, 이렇게 변수의 접근 가능한 범위를 스코프(scope; 범위)라고 함
- 여기에서는 변수 m은 main 전체에서 접근할 수 있기 때문에 스코프가 넓고 변수 x는 if문의 코드 블록안에서만 접근할 수 있기 때문에 스코프가 좁음
public class Scope1 {
public static void main(String[] args) {
int m = 10; //m 생존 시작
if (true) {
int x = 20; //x 생존 시작
System.out.println("if m = " + m); // 블록 내부에서 블록 외부는 접근 가능
System.out.println("if x = " + x);
} //x 생존 종료
// System.out.println("main x = " + x); // 오류, 변수 x에 접근 불가
System.out.println("main m = " + m);
} //m 생존 종료
}
2) 스코프 존재의 이유
(1) 이유
- 변수를 선언한 시점부터 변수를 계속 사용할 수 있을텐데 왜 복잡하게 접근 범위(스코프)라는 개념을 만든 이유는 메모리의 절약과 유지보수성을 높이기 위함임
- 아래의 코드는 좋은 코드라고 보기 어려운데 변수 temp는 if문 안에서 조건이 만족할 때만 임시로 잠깐 사용하는 변수인데 if문의 밖에서 main() 코드 블록에 선언되어있음
- temp는 main() 코드 블록이 종료될 때 까지 메모리에 유지되는데 실제 사용되는 공간은 if문의 코드블록이므로 불필요한 메모리가 낭비됨
- 또한, 좋은 코드는 군더더기가 없는 단순한 코드인데, temp는 if코드 블록에서만 필요하고 여기서만 사용하면 되기 때문에 if코드 블록안에 temp를 선언했다면 해당 if문이 종료된 이후에는 temp 변수는 전혀 신경쓰지 않을 수 있음
- 그러나 이렇게 밖에 선언하게되면 if문이 끝나도 main 범위안에서는 temp가 계속 살아있게되어 여전히 접근할 수 있게되고 누군가 해당 코드를 유지보수 할 때 m은 물론이고 temp까지 신경 써야 하게됨
- 이렇게 특정 범위안에서만 사용할 변수가 해당 범위를 벗어나는 스코프를 가지게 되면 실무에서 코드가 복잡하게 되어있을 때 코드를 한눈에 알아보기가 어렵고 유지보수가 어려워질 수 있음
public static void main(String[] args) {
int m = 10;
int temp = 0;
if (m > 0) {
temp = m * 2;
System.out.println("temp = " + temp);
}
System.out.println("m = " + m);
}
(2) 개선
- 이렇게 temp를 실제 사용하는 범위인 if문 안에서만 선언하면 temp를 신경써야하는 스코프가 줄어들게되어 메모리를 빨리 제거함으로써 메모리를 효율적으로 사용하고 범위도 적기때문에 유지보수하기도 좋은 코드가 되었음
public static void main(String[] args) {
int m = 10;
if (m > 0) {
int temp = m * 2;
System.out.println("temp = " + temp);
}
System.out.println("m = " + m);
}
(3) while문 vs for문의 스코프 관점
- 변수의 스코프 관점에서 while문과 for문의 카운터 변수의 위치에대해 비교해보면 while문의 경우 변수 i의 스코프가 main()메서드 전체가 되는 반면 for문에서는 for문의 구조 안으로 한정되게 됨
- 따라서 변수 i와 같이 for문 안에서만 사용되는 카운터 변수가 있다면 while문 보다는 for문을 사용해서 스코프의 범위를 제한하는 것이 메모리 사용과 유지보수관점에서 더 좋음
public static void main(String[] args) {
// while 반복문에서 사용되는 초기 변수가 외부에 선언되어있음
int i = 1;
while (i < 10) {
// 기타 코드 생략
i++;
}
// for 반복문에서 사용되는 변수가 for문 내부에 있음
for (int j = 0; j < 10; j++) {
// 기타 코드 생략
}
}
3) 스코프 정리
- 변수는 꼭 필요한 범위로 한정해서 사용하는 것이 좋음
- 변수의 스코프는 꼭 필요한 곳에서 한정으로 사용해야 메모리를 효율적으로 사용하고 더 유지보수하기 좋은 코드를 만들 수 있음
- 좋은 프로그램은 무한한 자유가 있는 프로그램이 아니라 적절히 제약이 있는 프로그램이며 무한한 자유가있는 프로그램은 무한한 버그가 있을 수 있으나 적절한 제약이 있는 프로그램은 좋은 설계, 좋은 API 스펙을 가질 수 있음
4) 자동 형변환
(1) 형변환
- 작은 범위에서 큰 범위로는 int -> long -> double 처럼 당연히 값을 넣을 수있음
- 큰 범위에서 작은 범위로의 형변환은 소수점 버림 및 오버플로우와 같은 문제가 발생할 수 있음
- double은 부동 소수점을 사용하기 때문에 int, long인 정수보다 더 큰 숫자 범위를 표현하기에 대입할 수 있음
- 즉 자바는 기본적으로 작은 범위에서 큰 범위로의 대입을 허용하며 이런것을 형변환이라고함
(2) 자동 형변환
- 하지만 결국 대입하는 타입(형)을 맞추어야하기 때문에 개념적으로는 아래의 코드처럼 동작하게 됨
- 즉 intValue의 앞에 (double)처럼 적어주면 int타입이 double타입으로 변환하면서 형변환이 동작하게 되는데 위에서처럼 작은 범위에서 큰 범위로의 대입은 개발자가 직접 형변환을 하지않아도 자동으로 동작하게 됨
- 이런 과정이 자동으로 일어난다고 해서 자동 형변환 혹은 묵시적 형변환이라고 함
int intValue = 10
double doubleValue = intValue
doubleValue = (double) intValue // 형 맞추기
doubleValue = (double) 10 // 변수 값 읽기
doubleValue = 10.0 // 형변환
5) 명시적 형변환
(1) 큰 범위에서 작은 범위로 형변환
- int형은 double형보다 숫자의 표현 범위가 작고 실수를 표현할 수도 없기 때문에 double타입을 int로 형변환하는 경우 숫자 손실이 발생하게되어 이것을 자동 형변환이 허용되게 되면 개발자의 실수로 인해 큰 버그를 유발할 수 있음
- 예를 들어 고객에게 은행이자를 계산해서 입금해야하는데 이런 코드가 아무런 오류 없이 수행된다면 비즈니스적으로 끔찍한 상황이 발생할 수 있기 때문에 자바는 이런 경우 컴파일 오류를 발생하여 오류를 빠르게 발견할 수 있음
(2) 강제로 형변환
- 하지만 이런 위험을 개발자가 알고있는 상황에서 감수하고 대입을 하고싶다면 강제로 타입을 변경할 수 있음
- 그럴때는 자동형변환이 진행되는 과정에서처럼 직접 형변환의 타입을 (int)처럼 명시적으로 사용하면 데이터 손실이 일어난 결과를 반환함
- 이렇게 개발자가 직접 형변환 코드를 입력한다고해서 명시적 형변환이라고함
double doubleValue = 1.5
int intValue = (int) doubleValue;
intValue = (int) 1.5; // doubleValue에 있는 값을 읽음
intValue = 1; // (int)로 형변환, intValue에 int형인 숫자 1을 대입
// doubleValue의 값은 그대로 1.5가 유지되고있음
** 참고
- 캐스팅은 영어단어 cast에서 유래되었으며 cast는 금속이나 다른 물질을 녹여서 특정한 형태나 모양으로 만드는 과정을 뜻하는데, 이렇게 타입을 바꾸는 것을 캐스팅이라고 함
(3) 형변환과 오버플로우
- 형변환을 할 때 표현할 수 있는 범위를 벗어나면 에러는 발생하지 않지만 전혀 다른 숫자가 표현하게되는데 이런 현상을 오버플로우라고함
- 오버플로우가 발생하면 마치 시계가 한바퀴 다시 돈 것처럼 다시 처음부터 시작을 하며, 변수의 타입의 범위의 가장 작은 값부터 남은 값들을 표현함
- 중요한 것은 오버플로우가 발생하는 것 자체가 문제이기때문에 오버플로우가 발생했을 때 결과가 어떻게 되는지 계산하는데 시간을 낭비하면 절대 안됨
- 오버플로우 자체가 발생하지 않도록 막으며 개발하는 것이 중요함
long maxIntOver = 2147483648L; // int 최고값 + 1
int intValue = (int) maxIntOver; // 변수 값 읽기
intValue = (int) 2147483648L; // 형변환 시도
// 오버플로우가 발생하여 남은 값인 1을 정수로 표현하여 정수의 가장 작은범위의 값이 출력됨
// 타입을 long으로 바꾸면 오버플로우가 발생하지 않음
intValue = -2147483648;
6) 계산과 형변환
- 형변환은 대입뿐 아니라 계산을 할 때에도 발생함
- 같은 타입끼리의 계산은 같은 타입의 결과를 내고 서로 다른타입의 계산은 큰 범위로의 자동 형변환이 일어난다는 원칙을 기억하면 됨
- 즉 int + long 은 long + long 으로 자동 형변환이 일어나고 int + double은 double + double로 자동 형변환이 일어난다고 이해하면됨
2. 훈련
1) 훈련 시작
(1) 의의
- 지금까지 학습한 변수, 연산자, 조건문, 반복문은 프로그래밍의 가장 기본이 되는 기능이기 때문에 프로그램언어는 해당 기능을 필수로 가지게 됨
- 그렇기에 지금까지의 내용을 익숙하게 잘 다룰수 있도록 기본기를 훈련하는 시간이며 사용자입력을 받는 Scanner을 학습하고 프로그래밍의 기본인 변수, 연산자, 조건문, 반복문을 다룰수 있도록 훈련을 진행
- 머리로만 생각하고 이해하는 것보다 몸으로 익히는 것이 더욱 중요하며 단순히 외우는 방식으로는 좋은 프로그래머가 될 수 없음
- 전부 따라해보고 스스로 문제를 풀어보는 능력을 키우며 문제를 못풀겠으면 따라 쳐보고 문제를 지우고 다시 문제를 푸는 복습과정을 거치는 것이 좋은 학습법이며, 머리로만 이해하고 넘어가는 것은 좋지 않음
- 백문이 불여일타!
2) Scanner 학습
(1) 사용자 입력
- System.out을 통해서 출력했듯이 System.in을 통해서 사용자의 입력을 받을 수 있음
- 그러나 자바가 제공하는 System.in을 통해서 사용자 입력을 받으러면 여러 과정을 거쳐야해서 복잡하고 어려움
- 자바는 이런 문제를 해결하기 위해 Scanner라는 클래스를 제공하며 이 클래스를 사용하면 사용자 입력을 매우 편리하게 받을 수 있음
(2) 예제
- Scanner scanner = new Scanner(System.in)은 객체와 클래스를 배워야 정확히 이해할 수 있어 지금은 Scanner의 기능을 사용하기 위해 필요한 필수 코드라고 이해하면되며 new 를 통해서 Scanner를 만든다 정도로 이해하고 넘어가면 됨
- Scanner scanner로 scanner 변수를 선언하였기 때문에 scanner 변수를 통해서 기능을 사용할 수 있음
- scanner.nextLine() : 엔터(\n) 즉, 개행문자가 들어올 때 까지 문자를 가져옴
- scanner.nextInt() : 입력을 int형으로 가져오고 정수 입력에 사용하며, 타입이 다르면 오류가 발생함
- scanner.nextDouble() : 입력을 double형으로 가져오고 실수 입력에 사용하며 마찬가지로 타입이 다르면 오류가 발생함
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("문자열을 입력하세요:");
String str = scanner.nextLine(); // 입력을 String으로 가져옴
System.out.println("입력한 문자열 = " + str);
System.out.print("정수를 입력하세요:");
int intValue = scanner.nextInt(); // 입력을 int로 가져옴
System.out.println("입력한 정수 = " + intValue);
System.out.print("실수를 입력하세요:");
double doubleValue = scanner.nextDouble(); // 입력을 double로 가져옴
System.out.println("입력한 실수 = " + doubleValue);
}
** 참고
- 출력문에서 print()와 println()의 차이는 개행문자가 포함되어있는지의 여부임
- print()로 사용하면 자동으로 개행문자가 없기 때문에 다음 라인으로 넘어가지 않음
- println()으로 사용하면 자동으로 개행문자가 포함되어있어 다름라인으로 넘어간 뒤에 다음의 코드가 실행되어 입력이나 출력시 다음 라인에서 실행 됨
2) Scanner 예제
(1) 예제 1
- 첫 번째 숫자와 두 번째 숫자를 더해서 출력하는 프로그램
- 첫 번째 숫자와 두 번째 숫자 모두 0을 입력하면 프로그램을 종료하고 프로그램은 반복해서 실행
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("첫 번째 숫자와 두 번째 숫자 모두 0을 입력하면 프로그램을 종료합니다");
while (true) {
System.out.println("첫번째 숫자를 입력하세요");
int num1 = scanner.nextInt();
System.out.println("두번째 숫자를 입력하세요");
int num2 = scanner.nextInt();
if (num1 == 0 && num2 == 0) {
System.out.println("프로그램 종료");
break;
}
int sum = num1 + num2;
System.out.println("두 숫자의 합 = " + sum);
}
}
(2) 예제2
- 사용자 입력을 받아 그 합계를 계산하는 프로그램
- 사용자는 한 번에 한 개의 정수를 입력할 수 있으며, 사용자가 0을 입력하면 프로그램은 종료됨
- 이 때 프로그램이 종료될 때 까지 사용자가 입력한 모든 정수의 합을 출력해야 함
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int sum = 0;
while (true) {
System.out.print("정수를 입력하세요 (0을 입력하면 종료) :");
int num = scanner.nextInt();
if (num == 0) {
break;
}
sum += num;
}
System.out.println("입력한 모든 정수의 합 = " + sum);
}
2) 문제와 풀이
(1) 음식점 주문
- 사용자로부터 음식의 이름(foodName), 가격(foodPrice), 수량(foodQuantity)를 입력받아 주문한 음식의 총 가격을 계산하고 출력하는 프로그램 작성
- 음식의 총 가격을 계산, 총 가격은 각 음식의 가격과 수량을 곱한값이며 이를 totalPrice라는 이름의 변수에 저장
- 주문 정보와 총 가격을 출력하고 출력 형태는 "[음식 이름][수량]개를 주문하셨습니다. 총 가격은 [총 가격]원 입니다." 여야 함
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("음식 이름을 입력해 주세요: ");
String foodName = scanner.nextLine();
System.out.print("음식 가격을 입력해 주세요: ");
int foodPrice = scanner.nextInt();
System.out.print("음식 수량을 입력해 주세요: ");
int foodQuantity = scanner.nextInt();
int totalPrice = foodPrice * foodQuantity;
System.out.println(foodName + " " + foodQuantity + "개를 주문하셨습니다. 총 가격은 " + totalPrice + "원 입니다.");
}
(2) 변수 값 교환
- 변수 a = 10 , b = 20일 때 a의 값과 b의값을 서로 바꿈
- temp 변수를 활용
public static void main(String[] args) {
int a = 10;
int b = 20;
int temp;
temp = b;
b = a;
a = temp;
System.out.println("a = " + a);
System.out.println("b = " + b);
}
(3) 사이 숫자
- 사용자로부터 두 개의 정수를 입력받고 두 정수 사이의 모든 정수를 출력하는 프로그램
- 사용자에게 num1의 변수에 첫 번재 숫자를 입력받고 num2의 변수에 두번째 숫자를 입력받음
- 만약 첫 번째 숫자 num1이 두번째 숫자 num2보다 크면 두 숫자를 교환
- num1부터 num2까지의 각 숫자를 출력하고 2, 3, 4, 5 처럼 ,로 구분해서 출력해야 함
- 반복문은 for / while 두가지 방법으로 해결
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("첫 번째 숫자를 입력하세요:");
int num1 = scanner.nextInt();
System.out.print("두 번째 숫자를 입력하세요:");
int num2 = scanner.nextInt();
if (num1 > num2) {
int temp = num1;
num1 = num2;
num2 = temp;
}
// while문으로 해결
System.out.print("두 숫자 사이의 모든 정수: ");
while (num1 < num2) {
System.out.print(num1 + ", ");
num1 += 1;
}
System.out.println(num2);
// for문으로 해결
System.out.print("두 숫자 사이의 모든 정수: ");
for (int i = num1; i <= num2; i++) {
System.out.print(i);
if (i != num2) {
System.out.print(", ");
}
}
}
(4) 상품 구매
- 사용자로부터 상품 정보(상품명, 가격, 수량)을 입력받고 이들의 총 가격을 출력하는 프로그램을 작성
- 사용자는 여러 상품을 추가하고 결제할 수 있으며, 프로그램을 언제든지 종료할 수 있음
- 사용자에게는 1: 상품 입력, 2: 결제, 3: 프로그램 종료 라는 옵션을 제공해야하며 옵션은 정수로 입력받고 옵션을 저장하는 변수의 이름은 option이여야 함
- 상품 입력 옵션을 선택하면 사용자에게 상품명과 가격 수량을 입력받아야 함
- 결제 옵션을 선택하면 , 총 비용을 출력하고 총 비용을 0으로 초기화(사용자가 총 비용을 확인하고 결제를 완료했다고 가정하여 총 비용을 다시 0으로 초기화)
- 프로그램 종료 옵션을 선택하면 "프로그램을 종료합니다"라는 메시지를 출력하고 프로그램을 종료
- 위의 세 가지 옵션 외에 다른 값을 입력하면 "올바른 옵션을 선택해주세요"라는 메시지를 출력
- 중간에 scanner.nextLine();의 코드가 있는 이유는 nextInt()에서는 개행문자가 없기 때문에 바로 다음 print 코드가 출력되어버리기 때문에 강제로 입력값에 개행을 만들어 주는 것
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int totalCost = 0;
while (true) {
System.out.println("1: 상품 입력, 2: 결제, 3: 프로그램 종료");
int option = scanner.nextInt();
if (option == 1) {
scanner.nextLine(); // 이전에 입력된 개행문자를 제거
System.out.print("상품명을 입력하세요: ");
String itemName = scanner.nextLine();
System.out.print("상품 가격을 입력하세요: ");
int itemPrice = scanner.nextInt();
System.out.print("구매 수량을 입력하세요: ");
int itemQuantity = scanner.nextInt();
totalCost += itemPrice * itemQuantity;
System.out.println("상품명: " + itemName +
", 가격: " + itemPrice +
", 수량: " + itemQuantity +
", 합계: " + itemPrice * itemQuantity);
} else if (option == 2) {
System.out.println("총 비용 = " + totalCost);
totalCost = 0;
} else if (option == 3) {
System.out.println("프로그램을 종료합니다.");
break;
} else {
System.out.println("올바른 옵션을 선택해주세요.");
}
}
}
'인프런 - 실전 자바 로드맵 > 자바 입문 - 자바 첫걸음(무료)' 카테고리의 다른 글
코드로 시작하는 자바 첫걸음4, 배열, 메서드 (2) | 2024.11.02 |
---|---|
코드로 시작하는 자바 첫걸음2, 조건문, 반복문 (1) | 2024.11.01 |
코드로 시작하는 자바 첫걸음1, 자바란, 변수, 연산자 (9) | 2024.10.31 |