Language/Java

static을 붙여 선언한 클래스 변수

Hoplin 2019. 2. 21. 02:55
반응형

인스턴스 멤버에는 인스턴스 변수, 인스턴스 메소드 이 두가지가 존재하였다. 그중 인스턴스 변수는 인스턴스를 생성하게 되면 생성하는 변수를 의미한다. 만약 '클래스 변수' 라고 한다면 이는 당연히 클래스가 생성되면 생성되는 변수를 의미할 것이다. 이번에는 클래스 변수에 대해 살펴보고자 한다.


클래스 내 선언된 변수 앞에 'static'이라는 키워드를 붙이게 되면 '인스턴스 변수'가 아닌 '클래스 변수'가 된다. 예시코드와 결과값을 한번 살펴보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class clasv {
    static int a = 0;
    clasv() {
        a += 1;
        System.out.println("인스턴스 총 개수  :" + a);
    }
}
 
class ma{
    public static void main(String[] args)
    {
        clasv a = new clasv();
        clasv b = new clasv();
        clasv c = new clasv();
    }
}
cs



해당 코드를 해석해 보면 clasv 클래스의 생성자는 인스턴스 하나가 만들어 질 때 마다 클래스 변수 a의 값을 하나씩 증가시키는 기능을 한다는 것을 볼 수 있다, 하지만 하나 수상한 점을 볼 수 있다. clasv클래스에 대해 인스턴스 a,b,c 를 생성 하였고 각각의 독립적인 인스턴스인데 최종적인 c에 대한 출력값을 보면 인스턴스 개수가 총 3개라고 출력되는것을 볼 수 있다. 이말은 즉슨 


클래스 변수는 해당 변수가 선언된 클래스의 모든 인스턴스가 함께 공유되는 변수


라는 것이다.




또 하나 클래스 변수에 대한 사실은 클래스 변수는 어떠한 인스턴스에도 속하지 않은 상태에서 메모리 공간에서 하나만 존재하는 변수라는 것이다. 클래스 변수 또한 앞에서 보았던 접근 수준 지시자에 영향을 받기에 public으로 하면 해당 패키지 이외 다른 패키지에서도 참조가 가능해 진다.

그렇다면 클래스 변수에 접근하기 위해서는 어떻게 해야할까? 클래스 변수에 접근한는 방법은 두가지가 존재한다. 


1 . 내부접근 : 변수 이름을 통한 접근


2 . 외부접근 : 클래스명 혹은 인스턴스 명을 통한 접근


클래스 변수 접근에 대한 예제 코드를 한번 살펴보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class getclvar {
    static int num = 0;
    getclvar(){
        access();
    }
    void access() {
        num ++;
    }
}
 
class ma{
    public static void main(String[] args) {
        getclvar a = new getclvar();
        a.num += 1//외부의 인스턴스 이름을 통한 접근
        getclvar.num += 1//외부의 클래스의 이름을 통한 접근
        System.out.println("number : " + getclvar.num);
    }
}
cs

우선 이 코드에서 보면 7번줄 num++은 내부 접근이 되는것이고 14,15번줄은 각각 인스턴스명, 클래스 명을 통한 접근, 즉 외부 접근이 되는것을 볼 수 있다. 해당 코드의 결과값은 다음과 같다.


결과값이 왜 3이 나왔는지 유도해 보자. 우선 13번째줄 getclvar a = new getclvar()이라는 부분을 통해서 인스턴스가 생성됨과 동시에 클래스 변수 num이 1만큼 더해진다. 그 후 14번째 줄에서 a.num += 1 부분은 13번째 줄에서 생성한 인스턴스를 통해 외부 접근 방식으로 1만큼 더해주었고, 15번쨰 줄에서 getclvar.num += 1 부분을 통해서 클래스 명을 통한 외부접근으로 클래스변수 num의 값에 1만큼 더해주었다. 결론적으로 클래스 변수에는 1이 총 3번 더해지게 된것이다.

하나 짚고 넘어가자 위의 코드에서 클래스 변수 num은 현재 default형식으로 선언되어있는것이다. 즉 동일 패키지에서는 해당 클래스 변수를 참조할 수 있다는 의미가 된다. 혹시라도 static을 보고 접근 수준지시자를 생각할 수 도 있어서 다시 한번 짚고 넘어가겠다.

클래스 변수는 결론적으로 인스턴스 변수와의 아무런 상관관계도 가지고 있지 않는것이다. 그렇다면 클래스 변수는 언제 초기화와 값의 할당이 이루어질까? 결론적으로 클래스 변수는 인스턴스 생성 이전에 메모리 공간에 존재하게 된다. 이 말은 클래스 변수는 클래스 정보가 **가상머신에 읽혀질때 순간 메모리 공간에 할당되며 초기화 된다는 의미이다. 또 확실한 것은 클래스변수의 할당과 초기화는 인스턴스 생성과는 아무런 상관관계없이 이루어 진다는 것이다. 즉 우리가 주의해야할 것은 생성자(Constructor)을 통한 클래스 변수 초기화는 잘못된 초기화 방법이라는것을 인지해두자.

**클래스 로딩 : 클래스 로딩이란 가상머신이 특정 클래스 정보를 읽는 행위를 의미한다.

그렇다면 클래스 변수는 결론적으로 어느 상황에서 사용하는것이 좋은건가? 우선 앞에서 보았던 예제를 통해 결론을 내자면 독립적인 인스턴스와 무관하게 데이터를 공유해야하는 상황에서는 클래스 변수를 사용하는것이 맞다. 그러면 클래스 변수를 유용히 쓸 수 있는 상황을 한번 살펴보자.

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
class circle{
    static final double pi = 3.14;
    private int rad;
    
    circle(int r)
    {
        rad = r;
    }
    void length() {
        double leng = (rad * 2* pi;
        System.out.println("length : " + leng);
    }
    void width() {
        double wid = (rad * rad) * pi;
        System.out.println("width : " + wid);
    }
}
 
class circleinfo{
    public static void main(String[] args)
    {
        circle a = new circle(6);
        a.length();
        a.width();
    }
}
cs

해당 코드에서는 원주율을 의미하는 PI는 불변의 값이기 때문에 final을 통해 상수처리를 해주었다. 그런데 앞에 static이 붙어져 있는걸 보니 '클래스 변수'라는 생각이 들것이다. PI값을 클래스 변수로 둔 이유는 간단하다. circle의 모든 인스턴스가 pi값을 참조해야하는건 맞지만 이 pi라는 값이 모든 인스턴스가 꼭 가지고 있어야 하는 값은 아니기 때문이다. 단지 동일하게 공유되는 값을 하나 가지고 인스턴스가 같이 공유하면서 사용하면 그게 더 효율적인 것이다. 즉 클래스 변수를 효율적으로 사용하기 위해서는 참조만을 위한 값에 대해서는 클래스 변수를 사용하는것이 더 효율적일 수 있다는 것이다.





반응형