이번에는 새로운 키워드인 static 키워드에 대해 알아보자.
static 키워드는 주로 맴버 변수와 메서드에 사용된다.
먼저 맴버 변수에 static 키워드가 왜 필요한지 이해하기 위해 간단한 예제를 만들어보자.
특정 클래스를 통해서 생성된 객체의 수를 세는 단순한 프로그램이다.
먼저 생성할 인스턴스 내부에 카운트를 저장하겠다.
Data1
package memory;
public class Data1 {
public String name;
public int count;
public Data1(String name) {
this.name = name;
this.count++;
}
}
생성된 객체의 수를 세어야 한다. 따라서 객체가 생성될 때 마다 생성자를 통해 인스턴스의 맴버 변수인 count 값을 증가시킨다.
(참고로 예제를 단순하게 만들기 위해 필드에 public 을 사용했다.)
DataCountMain1
package memory;
public class DataCountMain1 {
public static void main(String[] args) {
Data1 data1 = new Data1("data1");
System.out.println("data1.count: " + data1.count);
Data1 data2 = new Data1("data2");
System.out.println("data2.count: " + data2.count);
Data1 data3 = new Data1("data3");
System.out.println("data3.count: " + data3.count);
}
}
data1.count: 1
data2.count: 1
data3.count: 1
이 프로그램은 당연히 기대한 대로 작동하지 않는다. 객체를 생성할 때 마다 Data1 인스턴스는 새로 만들어진다. 그리고 인스턴스에 포함된 count 변수도 새로 만들어지기 때문이다.
이번에는 카운트 값을 저장하는 별도의 객체를 만들어보자
Counter
package memory;
public class Counter {
public int count;
}
Data2
package memory;
public class Data2 {
public String name;
public Data2(String name, Counter counter) {
this.name = name;
counter.count++;
}
}
DataCountMain2
package memory;
public class DataCountMain2 {
public static void main(String[] args) {
Counter counter = new Counter();
Data2 dataA = new Data2("A", counter);
System.out.println("A.count: " + counter.count);
Data2 dataB = new Data2("B", counter);
System.out.println("B.count: " + counter.count);
Data2 dataC = new Data2("C", counter);
System.out.println("C.count: " + counter.count);
}
}
A.count: 1
B.count: 2
C.count: 3
Counter 인스턴스를 공용으로 사용한 덕분에 객체를 생성할 때 마다 값을 정확하게 증가시킬 수 있다.
그런데 여기에는 약간 불편한 점들이 있다.
특정 클래스에서 공용으로 함께 사용할 수 있는 변수를 만들 수 있다면 편리할 것이다.
static 키워드를 사용하면 공용으로 함께 사용하는 변수를 만들 수 있다.
Data3
package static1;
public class Data3 {
public String name;
public static int count; // static
public Data3(String name) {
this.name = name;
count++;
}
}
기존 코드를 유지하기 위해 새로운 클래스 Data3 을 만들었다.
객체가 생성되면 생성자에서 정적 변수 count 의 값을 하나 증가시킨다.
static int count 부분을 보자. 변수 타입 (int) 앞에 static 키워드가 붙어있다.
이렇게 맴버 변수에 static 을 부이게 되면 static 변수, 정적 변수 또는 클래스 변수라 한다.
DataCountMain3
package static1;
public class DataCounterMain3 {
public static void main(String[] args) {
Data3 d1 = new Data3("data1");
Data3 d2 = new Data3("data2");
Data3 d3 = new Data3("data3");
System.out.println(Data3.count);
}
}
3
static 이 붙은 맴버 변수는 메서드 영역에서 관리된다.
static 이 붙은 맴버 변수 count 는 인스턴스 영역(힙 영역)에 생성되지 않는다. 대신에 메서드 영역에서 이 변수를 관리한다.
Data3
package static1;
public class Data3 {
public String name;
public static int count; // static
public Data3(String name) {
this.name = name;
count++;
}
}
예제 코드에서 name, count 는 둘다 맴버 변수이다.
맴버 변수(필드)는 static 이 붙은 것과 아닌 것에 따라 다음과 같이 분류할 수 있다.
맴버 변수(필드)의 종류
변수와 생명주기
static 이 정적이라는 이유는 바로 여기에 있다. 힙 영역에 생성되는 인스턴스 변수는 동적으로 생성되고, 제거된다. 반면에 static 인 정적 변수는 거의 프로그램 실행 시점에 딱 만들어지고, 프로그램 종료 시점에 제거된다. 정적 변수는 이름 그대로 정적이다.
정적 변수 접근법
package static1;
public class DataCounterMain3 {
public static void main(String[] args) {
Data3 d1 = new Data3("data1");
Data3 d2 = new Data3("data2");
Data3 d3 = new Data3("data3");
// 클래스를 통한 접근
System.out.println(Data3.count);
// 인스턴스를 통한 접근
System.out.println(d1.count);
System.out.println(d2.count);
System.out.println(d3.count);
}
}
3
3
3
3
static 변수는 클래스를 통해 바로 접근할 수도 있고, 인스턴스를 통해서도 접근할 수 있다.
그런데 인스턴스를 통한 static 변수는 추천하지 않는다. 왜냐하면 코드를 읽을 때 마치 인스턴스 변수에 접근하는 것 처럼 오해할 수 있기 때문이다.