Java

[인프런] static 변수

sejin2 2024. 5. 26. 19:37

static 키워드는 주로 멤버 변수와 메서드에 사용 된다.

public class Data1 {
    public String name;
    public int count;
    
    public Data1(String name) {
        this.name = name;
        count++;
    }
}
public class DataCountMain1 {
    public static void main(String[] args) {
        Data1 data1 = new Data1("A");
        System.out.println("A count = " + data1.count);

        Data1 data2 = new Data1("B");
        System.out.println("A count = " + data2.count);

        Data1 data3 = new Data1("C");
        System.out.println("A count = " + data3.count);
    }
}

기대한 값은 1, 2, 3 이렇게 나오는 것인데, 기대와 다르게 값이 나오는 것을 확인할 수 있다. 그 이유는 객체를 생성할 때 마다 Data1 인스턴스가 새로 만들어지기 때문에 그 안에 포함된 count 변수도 새로 만들어지기 때문이다. 

인스턴스에 사용되는 변수 count 값은 인스턴스끼리 서로 공유되지 않기 때문에 원하는 값을 얻을 수 없다. 
이를 해결하기 위해 변수를 서로 공유하도록 해야 한다. 

public class Counter {
    public int count;
}
public class Data2 {
    public String name;

    public Data2(String name, Counter counter) {
        this.name = name;
        counter.count++;
    }
}
public class DataCountMain2 {
    public static void main(String[] args) {
        Counter counter = new Counter();
        Data2 data1 = new Data2("A", counter);
        System.out.println("A count = " + counter.count);

        Data2 data2 = new Data2("B", counter);
        System.out.println("B count = " + counter.count);

        Data2 data3 = new Data2("C", counter);
        System.out.println("C count = " + counter.count);
    }
}

Counter 인스턴스를 공용으로 사용하여 객체를 생성할 때 마다 같은 값을 증가시킬 수 있다. 
그러나 이러한 방법은 Counter라는 별도의 클래스를 사용해야 하고, 생성자에 매개변수를 추가해야하는 불편함이 있다.
이를 내부에서 해결하기 위해 특정 클래스에서 공용으로 함께 사용할 수 있는 변수를 만들어야 한다. 
이때 static 변수를 사용하면 된다 !

public class Data3 {
    public String name;
    public static int count;

    public Data3(String name) {
        this.name = name;
        count++;
    }
}

멤버 변수에 static을 붙이게 되면, static 변수, 정적 변수 또는 클래스 변수라 한다.
객체가 생성되면 생성자에서 정적 변수 count의 값을 하나 증가시킨다. 

public class DataCountMain3 {

    public static void main(String[] args) {
        Data3 data1 = new Data3("A");
        System.out.println("A count = " + Data3.count);

        Data3 data2 = new Data3("B");
        System.out.println("B count = " + Data3.count);

        Data3 data3 = new Data3("C");
        System.out.println("C count = " + Data3.count);
    }
}

count 정적 변수에 접근하는 방법이 조금 특이한데, Data3.count와 같이 클래스 명에 .을 사용한다. 마치 클래스에 직접 접근하는 것처럼 보인다. 

  • static이 붙은 멤버 변수는 메서드 영역에서 관리한다. 
  • static이 붙은 멤버 변수 count는 인스턴스 영역에 생성되지 않고, 메서드 영역에서 이 변수를 관리한다.
  • Data3("A") 인스턴스를 생성하면 생성자가 호출된다. 
  • 생성자에는 count++ 코드가 있다. count는 static이 붙은 정적변수로 인스턴스 영역이 아니라 메서드 영역에서 관리한다. 따라서 이 경우 메서드 영역에 있는 count의 값이 하나 증가된다. 

이처럼 static 변수를 사용하여 공용 변수를 사용해서 편리하게 문제를 해결할 수 있다. 

멤버 변수(필드)의 종류

  • 인스턴스 변수 : static이 붙지 않은 멤버 변수
    • static이 붙지 않은 멤버 변수는 인스턴스를 생성해야 사용할 수 있고, 인스턴스에 속해 있다. 
    • 인스턴스 변수는 인스턴스를 만들 때 마다 새로 만들어진다.
  • 클래스 변수 :  static이 붙은 변수
    • 클래스 변수, 정적 변수, static 변수 등으로 부른다. 
    • static이 붙은 멤버 변수는 인스턴스와 무관하게 클래스에 바로 접근해서 사용할 수 있고, 클래스 자체에 소속되어 있기 때문에 클래스 변수라고 한다.  
    • 클래스 변수는 자바 프로그램을 시작할 때 딱 1개가 만들어진다. 인스턴스와는 다르게 여러 곳에서 공유하는 목적으로 사용된다.

변수와 생명주기

  • 지역 변수 ( 매개변수 포함 ) : 지역 변수는 스택 영역에 있는 스택 프레임 안에 보관된다. 메서드가 종료되면 스택 프레임도 제거되는데 이때 해당 스택 프레임에 포함된 지역 변수도 함께 제거 된다. 따라서 지역 변수는 생존 주기가 짧다.
  • 인스턴스 변수 : 인스턴스에 있는 멤버 변수를 말한다. 인스턴스 변수는 힙 영역을 사용한다. 힙 영역은 GC가 발생하기 전까지는 생존하기 때문에 보통 지역 변수 보다는 생존 주기가 길다.
  • 클래스 변수 : 메서드 영역의 static 영역에 보관되는 변수이다. 메서드 영역은 프로그램 전체에서 사용하는 공용 공간이다. 클래스 변수는 해당 클래스가 JVM에 로딩 되는 순간 생성되고, JVMM이 종료될 때까지 생명주기가 이어진다. 따라서 가장 긴 생명주기를 가진다. 

힙 영역에 생성되는 인스턴스 변수는 동적으로 생성되고 제거된다. 반면 static 정적 변수는 거의 프로그램 실행 시점에 만들어지고 프로그램 종료 시점에 제거된다. 

정적 변수에 접근할 때 인스턴스를 통한 접근과 클래스를 통한 접근 둘다 가능하다. 하지만 인스턴스를 통한 접근은 권장되지 않는데, 그 이유는 코드를 읽을 때 마치 인스턴스 변수에 접근하는 것처럼 오해할 수 있기 때문이다. 
정적 변수는 클래스에서 공용으로 관리하기 때문에 클래스를 통해서 접근하는 것이 더 명확하므로 정적 변수에 접근할 때는 클래스를 통해 접근하는 것이 좋다. 

 

 

공부 내용: [인프런] 김영한의 실전 자바 - 기본편

'Java' 카테고리의 다른 글

[인프런] final 변수와 상수  (0) 2024.05.27
[인프런] static 메서드  (0) 2024.05.26
[인프런] 스택과 큐 자료 구조  (0) 2024.05.25
[인프런] 자바 메모리 구조  (0) 2024.05.25
[인프런] 캡슐화  (0) 2024.05.22