본 글에 사용된 코드와 이미지의 일부는 김영한 님의 『김영한의 자바 입문』 강의를 참조하여 발췌·활용하였습니다.
스코프(Scope)
변수는 선언한 위치에 따라 지역 변수, 멤버 변수(클래스 변수, 인스턴스 변수) 와 같이 분류된다. 지역 변수 는 특정 지역에서만 사용할 수 있는 변수라는 뜻이고, 특정 지역을 벗어나면 사용할 수 없다.
특정 지역은 변수가 선언된 코드 블록( { } ) 이다. 지역 변수는 자신이 선언된 코드 블록 안에서만 생존하고, 자신이 선언된 코드 블록을 벗어나면 제거된다.
코드를 통해 살펴보자.
package scope;
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 생존 종료
}
int m 은 main{} 코드 블록안에서 선언되었기 때문에 변수를 선언한 시점부터 main{} 의 코드 블록이 종료될 때 까지 생존한다. 또한 if{} 블록 내부에서도 외부 블록에서 선언된 m 에 접근할 수 있다.
int x 는 if{} 블록안에서 선언되었기 때문에 if{} 의 코드 블록이 종료될 때 까지 생존한다.
이렇게 변수의 접근 가능한 범위를 스코프(Scope) 라 한다.
스코프 사용 이유
그렇다면, 변수를 선언한 시점부터 변수를 계속 사용할 수 있게 해도 되지 않을까? 왜 복잡하게 스코프(접근 범위) 라는 개념을 만들었을까?
코드를 통해 살펴보자.
public class Scope3_1 {
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 를 사용했다. 이 변수는 임시로 사용하는 변수인데 main() 코드 블록에 선언되어 있다. 이렇게 되면 다음과 같은 문제가 발생한다.
- 비효율적인 메모리 사용
- temp 는 if 코드 블록에서만 필요하지만, main() 코드 블록이 종료될 때 까지 메모리에 유지된다. 따라서 불필요한 메모리가 낭비된다.
- 코드 복잡성 증가
- 만약 if 코드 블록 안에 temp 를 선언했다면 if 가 끝나고 temp 를 전혀 생각하지 않아도 된다. 누군가 이 코드를 유지보수 할 때 신경써야 한다.
즉, 변수는 꼭 필요한 범위로 한정해서 사용하는 것이 좋다. 변수의 스코프는 꼭 필요한 곳으로 한정해서 사용하는 것이 메모리를 효율적으로 사용하고 더 유지보수하기 좋은 코드를 만들 수 있다.
형변환(Casting,캐스팅)
형변환
형변환의 특징을 살펴보자.
- 작은 범위에서 큰 범위로 값을 넣을 수 있다.
- int → long → double
- 큰 범위에서 작은 범위는 문제가 발생한다.
- 소수점 버림
- 오버플로우
자동 형변환
작은 범위 숫자 타입에서 큰 범위 숫자 타입으로의 대입은 개발자가 직접 형변환을 하지 않아도 되는데, 이런 과정은 자동을 일어나기 때문에 이를 자동 형변환 , 묵시적 형변환 이라고 한다.
어떻게 자동으로 변환되는지 코드로 살펴보자.
//intValue = 10
doubleValue = intValue
doubleValue = (double) intValue //형 맞추기
doubleValue = (double) 10 //변수 값 읽기
doubleValue = 10.0 //형변환
이렇게 앞에 (double) 과 같이 적어주면 int 형이 double 형으로 변한다.
명시적 형변환
반대로 큰 범위에서 작은 범위 대입은 명시적 형변환이라고 한다.
코드를 통해 살펴보자.
public class Casting2 {
public static void main(String[] args) {
double doubleValue = 1.5;
int intValue = 0;
//intValue = doubleValue; //컴파일 오류 발생
intValue = (int) doubleValue; //형변환
System.out.println(intValue); //출력:1
}
}
다음 코드의 앞부분에 있는 주석을 풀면 컴파일 오류가 발생한다. 왜 발생할까?
int 형은 double 형보다 숫자의 표현 범위가 작고 실수를 표현할 수도 없다. 따라서 이 경우 숫자가 손실되는 문제가 발생할 수 있다. 쉽게 큰 컵에 담긴 물을 작은 컵에 옮겨 담으려고 하니, 손실이 발생할 수 있다는 것이다.
하지만 이런 위험을 감수하고 값을 대입하고 싶다면 데이터 타입을 강제로 변경할 수 있다. 예를 들어, 소수점을 버리고 정수만 보고 싶을 수 있다. intValue = (int) doubleValue 이렇게 작성하면 된다.
명시적 형변환 과정을 코드로 살펴보자.
//doubleValue = 1.5
intValue = (int) doubleValue;
intValue = (int) 1.5; //doubleValue에 있는 값을 읽는다.
intValue = 1; //(int)로 형변환 한다. intValue에 int형인 숫자 1을 대입한다.
형변환과 오버플로우
형변환을 할 때 작은 숫자가 표현할 수 있는 범위를 넘어서면 어떻게 될까?
public class Casting3 {
public static void main(String[] args) {
long maxIntValue = 2147483647; //int 최고값
long maxIntOver = 2147483648L; //int 최고값 + 1(초과)
int intValue = 0;
intValue = (int) maxIntValue; //형변환
System.out.println("maxIntValue casting=" + intValue); //출력:2147483647
intValue = (int) maxIntOver; //형변환
System.out.println("maxIntOver casting=" + intValue); //출력:-2147483648
}
}
---실행 결과
maxIntValue casting=2147483647
maxIntOver casting=-2147483648
long maxIntValue 는 int 로 표현할 수 있는 가장 큰 숫자를 입력했기 때문에 long -> int 로 형변환을 해도 아무런 문제가 없다.
long maxIntOver 를 보면 int 로 표현할 수 있는 가장 큰 숫자에 1 큰 숫자를 입력해, int 범위를 넘어가기 때문에 마지막에 L 을 붙여서 long 형을 사용해야 한다.
결과가 -2147483648 이 나오는 이유는 int 형은 2147483648L 를 표현할 수 있는 방법이 없다. 이렇게 기존 범위를 초과해서 표현하게 되면 전혀 다른 숫자가 표현되는데, 이런 현상을 오버플로우 라고 한다. 보통 오버플로우가 발생하면 시계가 한바퀴 돈 것 처럼 다시 처음부터 시작한다. 그래서 저렇게 출력이 된다.
중요한 것은 오버플로우가 발생하는 것을 막는것이다. 위 경우 intValue 의 타입을 int -> long 으로 변경하면 된다.
계산과 형변환
형변환은 대입 뿐만 아니라, 계산을 할 때도 발생한다.
코드를 통해 살펴보자.
public class Casting4 {
public static void main(String[] args) {
int div1 = 3 / 2;
System.out.println("div1 = " + div1); //1
double div2 = 3 / 2;
System.out.println("div2 = " + div2); //1.0
double div3 = 3.0 / 2;
System.out.println("div3 = " + div3); //1.5
double div4 = (double) 3 / 2;
System.out.println("div4 = " + div4); //1.5
int a = 3;
int b = 2;
double result = (double) a / b;
System.out.println("result = " + result); //1.5
}
}
---출력 결과
div1 = 1
div2 = 1.0
div3 = 1.5
div4 = 1.5
result = 1.5
왜 저런 결과가 나올까?
이유는 다음과 같다.
- 같은 타입끼리의 계산은 같은 타입의 결과를 낸다.
- 서로 다른 타입의 계산은 큰 범위로 자동 형변환이 일어난다.
계산할 때 주의하도록 하자.
'인프런 > 김영한 자바' 카테고리의 다른 글
| [김영한의 자바 입문] - 7. 배열 (1) | 2025.09.01 |
|---|---|
| [김영한의 자바 입문] - 6. 입력 (0) | 2025.09.01 |
| [김영한의 자바 입문] - 4. 조건문 & 반복 (2) | 2025.08.28 |
| [김영한의 자바 입문] - 3. 연산자 (0) | 2025.08.27 |
| [김영한의 자바 입문] - 2. 변수 (0) | 2025.08.22 |