인스턴스가 생성되었을 때, 생성된 인스턴스 안에 존재하는 변수인 인스턴스 변수와 달리, 클래스 변수는 인스턴스의 생성과 상관없이 존재하는 변수
클래스 내에서 선언된 변수 앞에 static 선언을 붙이면 클래스 변수가 됨
클래스 변수는 인스턴스 내에 존재하는 변수가 아니라, 어떠한 인스턴스에도 속하지 않는 상태로 메모리 공간에 딱 하나만 존재하는 변수
이렇게 static으로 선언된 변수는 변수가 선언된 클래스의 모든 인스턴스가 공유하는 변수가 됨
class InstCnt {
static int instNum = 0;
InstCnt() {
instNum++;
System.out.println("인스턴스 생성: " + instNum);
}
}
class ClassVar {
public static void main(String args[]) {
InstCnt cnt1 = new InstCnt(); // 1
InstCnt cnt2 = new InstCnt(); // 2
InstCnt cnt3 = new InstCnt(); // 3
}
}
인스턴스 간에 데이터 공유가 필요한 상황에서 클래스 변수를 선언
또는 참조를 목적으로 존재하는 값은 final 선언이 된 클래스 변수에 담아 사용
예) 원주율을 값을 가진 PI
클래스 변수의 접근 방법
클래스 내부에서 접근할 경우에는 변수의 이름을 통해 직접 접근함
클래스 외부에서 접근할 경우에는 클래스 또는 인스턴스의 이름을 통해 접근함
class AccessWay {
static int num = 0;
AccessWay() {
increCnt();
}
// 클래스 내부 : 변수의 이름을 통해 직접 접근
void incrCnt() {
num++;
}
}
class ClassVarAccess {
public static void main(String args[]) {
AccessWay way = new AccessWay();
// 클래스 외부 : 인스턴스의 이름을 통해 접근
way.num++;
// 클래스 외부 : 클래스의 이름을 통해 접근
AccessWay.num++;
}
}
클래스 변수의 초기화 시점과 초기화 방법
클래스 변수는 인스턴스 생성 이전에 메모리 공간에 존재하므로 인스턴스의 생성과 무관하게 해당 클래스 정보가 가상머신에 의해 읽히는 순간 메모리 공간에 할당되고 초기화됨
그러므로 생성자를 통한 클래스 변수의 초기화를 진행하지 않도록 주의해야 함
✔ static 선언을 붙여서 선언하는 클래스 메소드
클래스 메소드(static 메소드)란?
클래스 내에 정의된 메소드에 static 선언을 붙이면 클래스 메소드가 됨
인스턴스 생성 이전부터 접근이 가능하므로 불필요한 인스턴스의 생성 과정을 생략할 수 있음
어느 인스턴스에도 속하지 않는 메소드
클래스 변수와 동일한 접근 방법을 가짐
class NumberPrinter {
private int myNum = 0;
static void showInt(int n) {
System.out.println(n);
}
// 클래스 내부 : 메소드의 이름을 통해 직접 접근
void showMyNumber() {
showInt();
}
}
class ClassMethod {
public static void main(String args[]) {
// 클래스 외부 : 클래스의 이름을 통해 접근
NumberPrinter.showInt(20);
// 클래스 외부 : 인스턴스의 이름을 통해 접근
NumberPrinter np = new NumberPrinter();
np.showInt(20);
}
}
클래스 메소드는 인스턴스에 속하지 않으므로 인스턴스 변수에 접근이 불가능하며, 인스턴스 메소드의 호출도 불가능
하지만 같은 클래스에 정의되어 있는 다른 클래스 메소드나 클래스 변수에는 접근이 가능함
✔ System.out.println() 그리고 public static void main()
out과 println의 정체는?
System은 자바에서 제공하는 클래스로 java.lang 패키지에 묶여 있음
원칙적으로는 java.lang.System.out.println이 되어야 하지만 컴파일러가 java.lang.* 패키지를 import 선언해주게 됨
그리고 out은 System.out으로 접근을 하니 static으로 선언된 클래스 변수
마지막으로 println은 PrintStream 클래스의 인스턴스 메소드
즉, 이는 System에 위치한 클래스 변수 out이 참조하는 인스턴스의 println 메소드를 호출하는 문장임을 알 수 있음
public final class System extends Object {
public static final PrintStream out;
...
}
main 메소드가 public이고 static인 이유는?
main 메소드의 호출이 이뤄지는 영역은 클래스 외부이므로 public으로 선언해야 함
그리고 main 메소드는 인스턴스가 생성되기 이전에 호출되므로 static으로 선언해야 함
이러한 main 메소드는 main 메소드를 담기 위한 별도의 클래스를 정의하는 것이 일반적
그러나 상황과 프로그래머의 성향에 따라서 다양한 형태로 어디든 존재할 수 있음
✔ 또 다른 용도의 static 선언
static 초기화 블록
클래스 변수에 대해서 선언과 동시에 초기화를 하고 싶다면 생성자는 적절하지 않음
이러한 상황을 고려하여 자바는 static 초기화 블록을 제공
이는 클래스 변수와 마찬가지로 가상머신이 클래스의 정보를 읽어 들일 때 실행이 되어 클래스 변수를 선언과 동시에 초기화할 수 있음
class DateofExecution {
static String date;
static {
LocalDate nDate = LocalDate.now();
date = nDate.toString();
}
public static void main(String args[]) {
System.out.println(date);
}
}
static import 선언
클래스 변수에 접근할 때 클래스 이름이 아닌, 클래스 변수의 이름만으로 접근하기 위해서는 static import를 선언함
최소한으로 사용할 경우 도움이 되지만, 빈번히 사용할 경우 해당 메소드 또는 변수가 어디에 정의되고 선언된 것인지 구분이 힘들어져 오히려 방해가 될 수 있음