들어가며
'인스턴스 변수'는 인스턴가
생성되었을 때,
생성된 인스턴스 안에 존재하는
변수이다.
그러나 '클래스 변수'는 인스턴스의
생성과 상관없이 존재하는 변수이다.
선언된 클래스의 모든 인스턴스가 공유하는 '클래스 변수(static 변수)'
클래스 내에 선언된 변수
앞에 static 선언을 붙이면
이는 인스턴스 변수가 아닌
'클래스 변수'가 된다.
이러한 클래스 변수의 특성을
파악하기 위해서
다음 예제를 관찰하자.
이 예제는 클래스 변수의 이해에
초점이 맞춰져 있어서
이 예제만으로도 클래스 변수의
기본 특성을 파악할 수 있을 것이다.
class InstCnt{
static int instNum = 0; // 클래스 변수 (static 변수)
InstCnt(){ // 생성자
instNum++; // static으로 선언된 변수의 값 증가
System.out.println("인스턴스 생성: " + instNum);
}
}
class ClassVar{
public static void main(String[] args){
InstCnt cnt1 = new InstCnt();
InstCnt cnt2 = new InstCnt();
InstCnt cnt3 = new InstCnt();
}
}
/*
실행 결과
인스턴스 생성: 1
인스턴스 생성: 2
인스턴스 생성: 3
*/
클래스 InstCnt의 생성자에서
static으로 선언된 변수
instNum의 값을 하나 증가시킨
다음에 결과를 출력하고 있다.
그런데 출력 결과를 보면 그
값이 인스턴스 생성 시마다
1씩 증가함을 알 수 있다.
그리고 이를 통해 다음 사실을
알 수 있다.
static으로 선언된 변수는 변수가 선언된
클래스의 모든 인스턴스가 공유하는 변수이다.
클래스 변수는 인스턴스 내에
존재하는 변수가 아니라
어떠한 인스턴스에도 속하지 않는
상태로 메모리 공간에 딱 하나만
존재하는 변수
이다.
다만 이 변수가 선언된 클래스의
인스턴스들은
이 변수에 바로 접근할 수 있는
권한이 있을 뿐이다.
그리고 클래스 변수도 '접근 수준
지시자'의 규칙을 그대로 적용받기
때문에
public으로 선언되면 어디서든
접근이 가능하다.
물론 접근 방법에 있어서는
차이를 보이는데
이와 관련된 내용은 이어서
설명하겠다.
클래스 변수의 접근 방법
클래스 변수에 접근하는 방법은
접근 영역을 기준으로
다음과 같이 크게 2가지로 나뉜다.
- 클래스 내부 접근 변수의 이름을 통해 직접 접근
- 클래스 외부 접근 클래스 또는 인스턴스의 이름을 통해 접근
그럼 다음 예제를 통해서
클래스 변수의 접근 방법을
모두 보이겠다.
class AccessWay {
static int num = 0;
AccessWay() {
incrCnt();
}
void incrCnt() {
num++; // 클래스 내부에서 이름을 통한 접근
}
}
class ClassVarAccess {
public static void main(String[] args){
AccessWay way = new AccessWay();
way.num++; // 외부에서 인스턴스의 이름을 통한 접근
AccessWay.num++; // 외부에서 클래스의 이름을 통한 접근
System.out.println("num = " + AccessWay.num);
}
}
/*
실행 결과
num = 3
*/
위의 예제에서 보이듯이,
앞서 보였던 클래스 내부에서의
접근 방법 이외에
다음과 같이 클래스의 이름
또는 인스턴스의 이름을 통한
접근도 가능하다.
way.num++; // 외부에서 인스턴스의 이름을 통한 접근
AccessWay.num++; // 외부에서 클래스의 이름을 통한 접근
인스턴스의 이름을 통한 접근
방법을 보면서,
클래스 변수를 인스턴스 내부에
위치한 것으로 오해하면 안 된다.
그리고 클래스 변수 num은
default로 선언되었다.
따라서 클래스 내부는 물론
클래스 외부에서도
동일 패키지로 묶여 있으면
접근이 가능하다.
클래스 변수의 초기화 시점과 초기화 방법
클래스 변수는 인스턴스의
생성과 상관이 없다고 했다.
그렇다면 클래스 변수는 언제
메모리 공간에 할당되고
초기화 되는가?
이와 관련하여 다음 예제를 보자.
class InstCnt {
static int instNum = 100;
InstCnt() {
instNum++;
System.out.println("인스턴스 생성: " + instNum);
}
}
class OnlyClassNolnstance {
public static void main(String[] args){
InstCnt.instNum -= 15; // 인스턴스 생성 없이 instNum에 접근
System.out.println(InstCnt.instNum);
}
}
/*
실행 결과
85
*/
위의 예제를 통해서 언급하고
싶은 내용은 다음과 같다.
클래스 변수는 인스턴스 생성
이전에 메모리 공간에 존재한다.
결론을 말하면, 클래스 변수는
해당 클래스 정보가
가상머신에 의해 읽히는순간
메모리 공간에 할당되고
초기화된다.
그리고 한 가지 확실한 것은
이러한 할당과 초기화는
위의 예제에서 보이듯이,
인스턴스의 생성과 무관하게
이뤄진다는 점이다.
따라서 다음과 같이
생성자를 통한 클래스 변수의
초기화를 진행하지 않도록
주의해야 한다.
class InstCnt {
static int instNum = 100; // 클래스 변수의 정상적인 초기화 방법
InstCnt() {
instNum = 0; // 클래스 변수의 초기화가 아님
}
}
위의 클래스 변수 instNum은
100으로 초기화된다.
클래스 정보가 가상머신에 의해
읽히는 순간 100으로 초기화된다.
그런데 생성자에서 변수 instNum을
0으로 다시 초기화 한다.
따라서 인스턴스가 생성될 때마다
instNum은 매번 그 값이 0으로
바뀌게 된다.
클래스 로딩(Class Loading)
앞서 설명에서 '클래스 정보를
가상 머신이 읽는다.'는 표현을
썼는데,
이렇듯 가상 머신이 특정 클래스
정보를 읽는 행위를 가리켜
'클래스 로딩(Class Loading)'이라
한다.
그리고 특정 클래스의 인스턴스
생성을 위해서는
해당 클래스가 반드시 가상 머신에
의해 로딩되어야 한다.
즉, 인스턴스 생성보다 클래스
로딩이 선행되는 것이다.
클래스 변수를 언제 유용하게 활용할 것인가?
앞서 제시했던 예제를 통해서도
클래스 변수가
유용하게 활용되는 상황 한 가지를
짐작할 수 있다.
인스턴스 간에 데이터 공유가
필요한 상황에서 클래스 변수를 선언한다.
앞서 예제에서는 '생성된 인스턴스의
수를 관리하는 상황'을 보였는데,
이 역시 인스턴스 간 데이터
공유의 한 사례에 해당한다.
그럼 이어서 클래스 변수가
유용하게 사용되는 사례를
하나 더 보이겠다.
이는 '클래스의 내부와 외부에서
참조해야 할 정보'를 클래스 변수에
담은 예이다.
class Circle {
static final double PI = 3.1415; // 변하지 않는, 참조가 목적인 값
private double radius;
Circle(double rad) {
radius = rad;
}
void showPerimeter() {
double peri = (radius * 2) * PI;
System.out.println("둘레: " + peri);
}
void showArea() {
double area = (radius * radius) * PI;
System.out.println("넓이: " + area);
}
}
class CircleConstPI {
public static void main(String[] args){
Circle c = new Circle(1.2);
c.showPerimeter();
c.showArea();
}
}
/*
실행 결과
둘레: 7.5396
넓이: 4.52376
*/
위 예제에서 PI가 상수로
선언이 되었다.
PI가 지닌 값은 '원주율'로,
결코 변하지 않는 값이기
때문이다.
그런데 인스턴스 변수가 아닌
'클래스 변수'로 선언되었다.
이는 모든 Circle 인스턴스가
참조해야 하는 값이지만,
인스턴스가 각각 지녀야 하는
값은 아니기 때문이다.
참조를 목적으로만 존재하는
값은 final 선언이 된 클래스
변수에 담는다.
게다가 이 값은 외부에서
참조한다고 해서 문제가
되는 값이 아니다.
그래서 private로 선언하지 않았다.
필요하다면 public으로 선언해도
괜찮은 값이다.
참고 및 출처
|