클라이언트/ 서버/ 엔지니어 " 게임 개발자"를 향한 매일의 공부일지

클래스와 객체 14 - static 멤버 1 : static 멤버의 생성 및 활용 2가지 방법 본문

프로그래밍 언어/자바

클래스와 객체 14 - static 멤버 1 : static 멤버의 생성 및 활용 2가지 방법

huenuri 2024. 8. 19. 00:22

드디어 static에 대한 학습을 시작하려고 한다. 엘리스로 기본 내용을 학습했으니 이 공부도 충분히 잘할 수 있을 것이다. 지금 11시 반이 조금 안 되었는데 4장 학습을 1시간 반 안에 마치고 1시에는 취침해보려고 한다.


 

 

 

 

1. 눈은 각 사람마다 있고 공기는 모든 사람이 소유(공유)한다

사람은 모두 각자의 눈을 가지고 태어난다. 각자의 눈은 각 사람의 개별적 소유이다. 공기는 자신의 소유인 것처럼 들이마시고 내뱉으며 사용할 수 있다. 모든 사람이 공유하는 것이다. 눈과 공기 모두 각 사람이 소유하는 요소라는 공통점이 있다. 하지만 눈은 각 사람마다 있고 공유하지 않고, 공기는 오직 하나만 있어서 모든 사람이 공유한다는 차이점이 있다.

결론적으로 말하면 눈은 사람이라는 객체의 non-static 멤버이며 공기는 static 멤버이다.

 

 

 

2. static 멤버의 선언

여기서는 static으로 선언된 멤버의 특성과 활용을 설명하고자 한다. static 멤버는 다음과 같이 static을 붙여 선언한다.

class StaticSample {
    int n;			// non-static 필드
    void g() {...}		// non-static 메소드
    
    static int m;		// static 필드
    static void f() {...}	// static 메소드
}

 

static으로 선언된 멤버는 non-static 멤버와 매우 다른 특성을 가진다.


 

 

 

3. non-static 멤버와 static 멤버의 차이점

static 멤버는 객체를 생성하지 않고도 사용할 수 있는 멤버이다. 또한 클래스 하나만 생성되는 메버로서 동일한 클래스의 모든 객체들이 공유한다. main() 메소드가 실행되기 전에 이미 생성한다. 또한 static 멤버가 포함된 객체를 생성하기 전에도 사용할 수 있다.

반면 non-static 멤버는 객체가 생길 때 객체마다 생기며, 다른 객체들과 공유하지 않는다. 객체가 사라지면 non-static 멤버도 함께 사라지고 더 이상 접근할 수 없다. static 멤버는 클래스당 하나씩 생긴다고 해서 클래스 멤버라고도 부르며, non-static 멤버는 각 객체마다 하나씩 생긴다고 해서 인스턴스 멤버라고 부른다.

 

 


 

 

 

 

4. static 멤버의 생성과 활용 1: 객체.static 멤버

static 멤버를 가진 클래스의 객체가 생성되는 과정을 통해 static 멤버와 non-static 멤버에 대해 보다 자세히 알아보자. StaticSample 클래스는 2개의 static 멤버를 가지고 있다.

 

static 멤버의 생성

static 멤버가 생성되는 시점은 StaticSample이 사용되기 시작하는 시점이다. 다음 코드가 실행되는 시점에는 static 멤버m과 f()는 이미 존재하며 사용이 가능하다.

StaticSample s1, s2;

 

다음 코드는 2개의 StaticSample 객체를 생성하는 코드이다.

s1 = new StaticSample();
s2 = new StaticSample();

 

static 멤버 m과 f()는 이들 두 객체가 생성되기 이전에 이미 생성되어 있으므르, s1과 s2 객체가 생성될 때 인스턴스 멤버인 n, g(), h()만 객체마다 생성된다.


 

 

 

static 멤버 접근

static 멤버도 멤버이기 때문에 다음과 같이 non-static 멤버와 사용 방법이 동일하다.

객체.static필드
객체.static메소드

 

다음 코드는 s1, s2 객체의 static 멤버를 접근한다.

s1.m = 50;
s2.f();

 

 

 

static 멤버의 공유

객체 s1과 객체 s2는 static 멤버 m과 f()를 공유하고 또한 자신의 멤버라고 생각한다. g(), h()에서도 static 멤버 m을 공유하고 있는 것을 볼 수 있다.

 


 

코드 실행 과정 설명

 

1. 클래스 정의 및 초기화

  • StaticSample 클래스는 int 타입의 인스턴스 변수 n과 두 개의 인스턴스 메서드 g()와 h()를 가진다.
  • static 변수 m과 static 메서드 f()도 포함되어 있다.
  • static 변수는 클래스가 로드될 때 초기화되고, 모든 인스턴스에서 공유된다.

2. 객체 생성 및 첫 번째 객체 s1 사용

  • s1이 new StaticSample()로 생성된다.
  • s1.n = 5;로 인스턴스 변수 n이 5로 설정된다.
  • s1.g()를 호출하면, static 변수 m이 20으로 변경된다.
  • 그림 설명: 이 시점에서 m은 20으로 설정된다. s1.m = 50;를 실행하면 static 변수 m의 값이 50으로 변경된다.

3. 두 번째 객체 s2 사용

  • s2가 new StaticSample()로 생성된다.
  • s2.n = 8;로 인스턴스 변수 n이 8로 설정된다.
  • s2.h()를 호출하면 static 변수 m이 30으로 변경된다.
  • 그림 설명: s2.h() 호출로 m이 30으로 설정된다. 그리고 s2.f()를 호출하면 m이 다시 5로 변경된다.

4. System.out.println(s1.m);

  • s1.m을 출력하면 5가 출력된다.
  • 그림 설명: s2.f() 호출로 인해 m의 값이 5로 변경되었기 때문에, s1.m을 출력해도 5가 출력된다. static 멤버는 모든 인스턴스에서 공유되기 때문이다.

 

정리

  • static 멤버는 클래스 수준에서 관리되며, 어떤 객체를 통해 접근하든 동일한 값을 공유한다.
  • 인스턴스 멤버는 각 객체마다 별도로 관리된다.
  • 이 예제에서는 m이 static 변수이기 때문에 s1과 s2 모두 동일한 m을 공유하고 있다. 따라서 s2가 f()를 호출하여 m을 5로 변경한 후, s1.m을 출력하면 5가 출력된다.

 

 

각 단계별 그림을 해석해보겠다. 

1. 첫 번째 그림 (s1 객체 생성 후 초기화)

  • s1 = new StaticSample();
    • s1이라는 객체가 생성되고, n은 초기화되지 않았지만, 초기화가 되어도 별도로 static 변수와 연관되지 않는다.
  • s1.n = 5;
    • s1 객체의 인스턴스 변수 n이 5로 설정된다. 이 값은 s1 객체에만 영향을 미친다.
  • s1.g();
    • s1.g()가 호출되면, static 변수 m이 20으로 설정된다. 여기서 주의할 점은 m이 static이므로 s1뿐만 아니라 모든 StaticSample 객체에서 공유된다는 점이다.

2. 두 번째 그림 (s1 객체의 static 변수 변경)

  • s1.m = 50;
    • s1을 통해 static 변수 m의 값을 50으로 변경한다.
    • static 변수는 클래스에 속해 있기 때문에, 이 변경은 모든 StaticSample 객체에 영향을 미친다. 즉, 이후에 생성될 모든 객체도 이 변경된 값을 보게 된다.

3. 세 번째 그림 (s2 객체 생성 및 초기화)

  • s2 = new StaticSample();
    • 이제 s2라는 새로운 객체가 생성된다. 이 객체는 s1과 독립된 인스턴스 변수 n을 가진다.
    • s2.n = 8;로 설정되어 s2 객체의 n이 8이 된다.
  • s2.h();
    • s2.h()를 호출하면, m이 30으로 변경된다. 이 역시 static 변수이기 때문에 모든 객체에 영향을 준다.

4. 네 번째 그림 (s2 객체의 static 메서드 호출)

  • s2.f();
    • s2를 통해 static 메서드 f()를 호출하면, m이 5로 변경된다. f() 메서드는 static 변수 m만을 변경한다.

5. 마지막 단계 (System.out.println(s1.m);)

  • System.out.println(s1.m);
    • 여기서 출력되는 값은 5이다. s2가 f()를 호출한 후에 m이 5로 설정되었기 때문에, s1.m을 출력해도 5가 출력된다. 이는 m이 static 변수로서 모든 객체에서 공유되기 때문이다.

요약

  • 인스턴스 변수(n): 각 객체마다 독립적으로 존재하고, 객체별로 다른 값을 가질 수 있다.
  • static 변수(m): 클래스에 속해 있으며, 모든 객체에서 동일한 값을 공유한다. 하나의 객체가 이 값을 변경하면 다른 객체에도 영향을 미친다.
  • static 메서드(f()): static 변수만을 변경한다. 이 메서드를 어떤 객체에서 호출하든, static 변수는 같은 클래스에 속한 모든 객체에서 동일한 값으로 공유된다.

 

 

 

 

5. staitc 멤버의 생성 및 활용 2: 클래스명.static 멤버

 

static 멤버를 접근하는 또 다른 방법이 있다. static 멤버는 클래스당 하나만 있기 때문에 다음과 같이 클래스 이름으로 바로 접근할 수 있다.

클래스명.static멤버

 

앞의 코드를 다음과 같이 수정해본다. main() 메소드의 다음 코드는 '클래스명.static멤버'를 사용하는 실례와 new에 의해 객체가 생기기 전에 static 멤버를 접근할 수 있음을 보여준다.

StaticSample.m = 10;

 

static 메소드도 다음과 같이 2가지 방법으로 모두 접근 가능하다.

s1.f();			// 객체 레퍼런스로 static 멤버 f() 호출
StaticSample.f();	// 클래스명을 이용하여 static 멤버 f() 호출

 

하지만 non-static 메소다른 클래스 이름으로 접근할 수 없다.

 

 

그림 해석 및 설명

  1. StaticSample.m = 10;
    • 클래스가 로드되면 static 변수 m이 생성되고, 이 시점에서 m의 값이 10으로 설정된다.
    • static 변수는 클래스 로드 시점에 메모리에 할당되며, 프로그램이 종료될 때까지 메모리에 유지된다. 클래스의 모든 인스턴스에서 공유된다.
  2. StaticSample s1; s1 = new StaticSample();
    • s1이라는 StaticSample 객체가 생성된다. 이 객체는 인스턴스 변수 n을 가지며, static 변수 m을 공유한다.
    • 객체 s1이 생성되어도 m은 여전히 10이다.
  3. System.out.println(s1.m);
    • 여기서 s1.m을 출력하면 10이 출력된다. 이유는 m이 static 변수이기 때문에 클래스 수준에서 관리되며, 객체 s1과 독립적이지 않다.
  4. s1.f();
    • s1 객체를 통해 f() 메서드를 호출한다. f()는 static 메서드이기 때문에 객체의 상태와 무관하게 m의 값을 5로 변경한다.
    • 이 시점에서 m은 5가 된다.
  5. StaticSample.f();
    • 클래스 이름을 통해 직접 f() 메서드를 호출한다. 이 역시 static 메서드이므로 m의 값이 다시 5로 설정된다.
    • 이전 단계에서 m이 이미 5로 변경되었기 때문에, 이 호출은 m의 값을 변경하지 않는다.

 


 

 

 

 

학습을 마치고

static 분량이 많아서 다음 포스트에 이어서 하기로 했다. 코드에 대한 설명이 부족하여 따로 분석하는 작업을 했더니 내용이 많이 길어졌다.

그래도 static에 대해 어떻게 호출되고 값이 변경되는지 조금 이해할 수 있게 되었다. 아직 완벽히 이해하지는 못했지만, 처음부터 너무 완벽하게 공부하면 지치니까 처음 학습할 때는 이 정도로 충분한 것 같다.