✍ 본 포스팅은 책 ‘처음 해보는 자바 프로그래밍(오정임 저, 루비페이퍼)’을 참고하여 작성되었습니다.
static member : 모든 인스턴스가 공유하는 멤버. static
키워드를 이용하여 변수를 선언.
정적 멤버에는 클래스 필드
(=static 필드), 클래스 메서드
(=static 메서드), 정적 코드 블럭
이 있다.
static int total Count; // static 필드
public static void print1() { // static 메서드
System.out.println("hello");
}
static { // 정적 코드 블럭
System.out.println("hello");
System.out.println("java");
}
class Count {
public static int totalCount; // 클래스 필드. 프로그램 실행 시 코드(영역) 메모리에 할당
int count; // 인스턴스 필드. 인스턴스 생성 시 힙(영역) 메모리에 할당
}
필드 선언 시 static
키워드를 붙여 클래스 필드를 선언한다.
static 키워드를 붙여서 선언하였으므로, 프로그램 실행 시(메모리 할당 시점) 자동으로 코드 메모리(=메서드 메모리)에 할당되고, 프로그램 종료 시 메모리 해제가 된다.
/* 클래스 필드와 인스턴스 필드 비교 예제 */
// 하나의 소스파일에 여러 개의 클래스 선언이 가능하다.
// 단 소스 파일 이름은 public 으로 선언한 클래스 이름으로 지정해야한다.
class Count {
public static int totalCount; // 클래스 필드. 프로그램 시작 시 코드 메모리에 생성됨.
int count; // 인스턴스 필드. 인스턴스 생성 시 힙 메모리에 생성됨.
}
public class CountTest {
public static void main(String[] args) {
System.out.println("실행 시작");
Count c1 = new Count(); // 참조변수 c1은 스택 메모리에 생성. c1이 가리키는 인스턴스는 힙 메모리에 생성.
}
}
위 예제를 통해 프로그램이 실행되는 동안 각 필드와 변수들이 어느 메모리에 생성되는지를 이해해보자.
JVM의 클래스 로더는 main( ) 메서드가 시작되기 전에 로딩된 모든 클래스 코드를 살펴보고 static으로 선언된 정적 멤버를 코드 메모리에 할당한다.
totalCount
가 static으로 선언되었으므로, 메모리의 코드 영역에 할당된다.main( ) 메서드가 실행되고, new Count( );
명령문을 통해 Count 클래스의 인스턴스가 힙 메모리에 생성된다.
totalCount
는 힙 메모리에 할당하지 않는다(인스턴스 필드 count
는 힙 메모리에 할당된다). 클래스 필드는 이미 main( ) 메서드가 실행되기 전에 코드 메모리에 할당했기 때문이다.참조형 데이터 타입 Count 로 선언한 참조 변수 c1
은 스택 메모리에 생성되고, 힙 메모리에 있는 Count 인스턴스를 참조한다.
클래스 필드를 사용할 때는 참조변수가 필요 없다. main( ) 메서드가 실행되기 전에 이미 메모리에 할당되었으므로 바로 사용 가능하다.
클래스 필드는 클래스명.필드명
과 같이 사용한다.
메서드 선언부에 static 키워드가 선언된 메서드를 클래스 메서드
라고한다.
static 으로 선언되었으므로, main( ) 메서드가 실행되기 전에 코드(메서드) 메모리 영역에 생성되어 사용 준비가 완료된다.
(사용 이유) 인스턴스 생성과 무관하게 사용하는 메서드는 static으로 선언한다.
클래스명.메서드명
과 같이 사용한다.클래스 메서드에서는 인스턴스 필드와 인스턴스 메서드를 사용할 수 없다.
클래스 메서드는 인스턴스를 생성하지 않고 사용이 가능하지만, 인스턴스 필드와 인스턴스 메서드는 인스턴스 생성 이후에 사용가능하므로, 클래스 메서드에서 인스턴스 필드와 인스턴스 메서드를 사용할 수 없다 (클래스 메서드를 호출 시 인스턴스가 생성되어 있지 않을 수 있으므로).
인스턴스 메서드에서는 인스턴스 필드를 사용할 수 있다 (인스턴스 메서드 호출 시에는 이미 인스턴스가 생성되고 인스턴스 필드 역시 메모리에 생성되어있으므로)
/* 클래스 메서드와 인스턴스 메서드에서의 인스턴스 필드 사용 가능 여부 확인 예제 */
public class StaticMethodTest {
int num = 123; // 인스턴스 필드. 인스턴스 생성된 후 사용 가능하다.
public static void main(String[] args) {
StaticMethodTest.print1();
StaticMethodTest exam = new StaticMethodTest();
exam.print2();
}
public static void print1() { // static 메서드
/*
인스턴스 필드는 인스턴스를 생성한 후에 사용 가능하므로,
인스턴스 생성 전에 사용가능한 static 메서드에서는 인스턴스 필드와 인스턴스 메서드를 사용할 수 없다.
*/
int num2 = num; // 오류! : static 메서드에서는 인스턴스 필드 사용 불가능
System.out.println("hello");
}
public void print2() { // 인스턴스 메서드
/*
인스턴스 메서드를 호출하는 시점에서는 이미 인스턴스가 생성되어 인스턴스 필드가 준비되어 있는 상태이므로,
인스턴스 메서드 내부에서는 인스턴스 필드의 사용이 가능하다.
*/
int num3 = num; // 인스턴스 필드 사용
System.out.println();
}
}
public class StaticBlockTest {
// 정적 코드 블록. main() 메서드가 실행되기 전에 딱 한 번 실행된다.
static {
System.out.println("hello");
System.out.println("java");
}
public static void main(String[] args) {
System.out.println("world!");
}
}
/*
(실행 결과)
hello
java
world!
*/
정적 코드 블록 : static 으로 선언된 실행문 블럭
정적 멤버 처럼 main( ) 메서드가 실행되기 전에 딱 한 번 실행된다.
프로그램이 실행될 때 먼저 처리해야하는 작업을 정적 코드 블록에 작성한다.