static은 클래스에 속하고 객체 생성 없이 사용 가능한 멤버다.
static 키워드를 사용해서 static변수와 static메서드를 만들 수 있다. 이것들을 다른말로 정적필드와 정적 메서드라고도 하고 이 둘을 합쳐서 정적 멤버라고 한다.
저장 위치
정적 멤버는 메서드 영역에 저장된다.
메모리 로드 시점
클래스가 JVM에 로딩될 때 메모리에 할당된다.
class Counter {
static int count = 0;
int instanceCount = 0;
Counter() {
count++;
instanceCount++;
}
}
Counter c1 = new Counter();
Counter c2 = new Counter();
Counter c3 = new Counter();
System.out.println(Counter.count); // 3
System.out.println(c1.instanceCount); // 1
System.out.println(c2.instanceCount); // 1
위 코드를 보면 객체를 생성할 때마다 count와 instanceCount를 ++해주고 있는데 count는 모든 인스턴스가 공유하는 static 변수여서 객체를 생성할 때마다 계속 더해져 3이 되게 된다.
class Calculator {
static int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
return a * b;
}
}
int result1 = Calculator.add(5, 3); // 객체 생성 없이 호출 가능
Calculator calc = new Calculator();
int result2 = calc.multiply(5, 3); // 객체 생성 후 호출
위 코드를 보면 객체 생성 없이 static 메서드를 호출할 수 있다. 반면 인스턴스 메서드는 객체를 생성한 후에 호출해야 한다.
class InitExample {
static int value;
static {
value = 100;
System.out.println("static 블록 실행");
}
}
위 코드를 보면 static 블록은 클래스가 로딩될 때 자동으로 실행되고 value(static 변수)를 100으로 초기화한다.
static 블록은 복잡한 초기화 로직을 수행할 때 유용하다.
static은 메서드 영역에 저장되어 GC의 관리 대상이 아니다. 그래서 사용하지 않아도 계속 메모리에 남아있어 문제가 발생할 수 있다.
여러 스레드가 동시에 static 변수를 수정하면 Race Condition 문제가 발생할 수 있다. 그래서 synchronized로 동기화 처리를 해줘야 한다.