여러가지 이야기

[Java] static 멤버에 대해서 알아보자 본문

study/Java

[Java] static 멤버에 대해서 알아보자

jimddong 2024. 1. 23. 14:48

알고리즘 공부를 위해 백준의 문제를 풀려했는데, 나에게 익숙한 C언어 대신 이제는 저번 학기에 배운 자바를 공부할 겸 자바로 문제를 풀려고 했다. 그런데 자바로 코드를 짤려고 하니 다른 메소드를 어떻게 호출할지, 배열 같은 것을 계속 인자로 넘겨주어야할지 여러가지 문제와 마주하게 되었다... 이 과정에서 그러다 static 메소드는 인스턴스 메소드(= non-static 메소드)에 접근할 수 없다는 사실을 다시금 깨닫게 되었다. (완전히.. 망각하고 있었음 허허)

여튼간 예전에 공부했던 내용이어도 자꾸 까먹고 static 멤버와 관련된 개념이 자꾸 헷갈려, 공부한 내용을 정리하는 글을 작성해보려한다.

 

 

Static 멤버란

일단 static 멤버(클래스 멤버)가 무엇일까? static 멤버는 다음과 같은 형식으로 이루어져있다.

static int n; //static 필드(변수)
static void func(); //static 메소드

참고로 자바에서 필드와 변수를 구분짓자면 필드는 클래스 중괄호 안에 선언된 변수. 변수는 메소드 중괄호 안에 선언된 변수라고 한다.

 

static 멤버는 static 필드와 static 메소드로 이뤄진 영역으로, 동일 클래스 내의 모든 객체가 공유하는 전역 변수 혹은 전역 함수와 같은 존재이다. 

s1과 s2가 한 동일 클래스 내에 있는 객체들일 때, 같은 한 static 멤버를 공유한다.

 

 

이때, static 멤버는 클래스 당 단 하나만 존재할 수 있다. 따라서 클래스 멤버라고도 부른다.

(+ 여기서 '클래스 당 하나'의 의미는 static 메소드나 변수가 각각 하나씩이라는 뜻이 아니라, 정적 메소드와 변수들이 포함되는 static 멤버 영역을 의미한다. 즉 static 메소드와 변수들의 개수는 이 영역 안에서 여러 개여도 가능하다. 난 처음에는 static 멤버 안에서 정적 메소드와 변수가 하나씩만 있어야만 한다는 줄... 착각했다...)

난 위처럼 static 멤버를 한 영역처럼 이해했다.

 

또한 static 멤버는 non-static 멤버와 달리 생성되는 시점이 다르다. 그냥 생각했을 때 non-static(instance) 멤버처럼 main 함수가 실행되고 여기서 어떤 객체가 생겼을 때 static 멤버도 할당될 것 같지만 그렇지 않다. static 멤버는 객체가 생기기도 전에 생긴다! 왜냐하면 클래스 로딩 시에 생기기 때문이다! 따라서 당연히 객체를 만들지 않고도 static 멤버를 사용할 수 있다.

이 시간적 특징과 관련해서는 좀 이따가 추후에 나올 static 멤버를 코드에 쓸 때 주의할 점과 깊게 관련이 있기에 일단 기억해두자.

 

더보기

static 멤버 특징 정리

 

  • 동일 클래스는 한 static 멤버를 공유한다. (공유의 특성)
  • 클래스 당 단 하나만 가능하다. (공간적 특성)
  • 클래스가 로딩될 때 공간이 할당된다. (시간적 특성)

 

 

Static 멤버의 활용

그럼 언제 static 멤버를 쓰면 될까? 앞서 static 멤버의 특징을 설명한 내용 중에서 쉽게 떠올려 볼 수 있다.

 

- 전역 변수와 전역 함수를 만들고 싶을 때(공유 멤버로 활용할 때)

static int N;
static int []arr;

클래스 내에 하나만 존재할 수 있는 static 멤버는 동일한 클래스 내에 있는 객체들이 모두 공유할 수 있다.(공유의 특성) 즉 모두가 공유하는 전역변수, 전역함수 또는 공유 멤버로서 활용할 수 있을 것이다. 위 코드처럼 static으로 선언해주면 N과 arr은 전역 변수로서 활용이 가능할 것이다.

 

- 객체를 생성하지 않고 코드를 짜고 싶을 때 -> 클래스 이름으로 static 멤버에 접근

코드에 따라 굳이 객체를 생성(new 어쩌고~)하지 않아도 될 때가 있을 것이다. 그냥 전역 변수 및 전역 함수로 static을 활용한다.

static 멤버를 객체의 멤버(ex. 객체 s1에 대해 객체의 멤버인 static 필드 m을 s1.m로 접근)로 하여 접근 / 클래스 이름으로 접근(ex. ClassName.m)하는 방법 중 후자가 이에 해당한다. 자세한 것은 코드로 비교해보자.

 

 

code 1. static 멤버를 객체의 멤버로 하여 접근

// 인스턴스 멤버 생성한 뒤 그 객체의 멤버인 static 멤버를 사용함.
class InstanceMem {
	public static int m;
        public static void func(){
            m = 20;
        }
}

public class Main {
	public static void main(String[] args){
    	InstanceMem s1 = new InstanceMem();
        s1.m = 10; // m이 10
        s1.func(); // m이 20
        .
        .
        .
    }
}

위 코드에서는 생성자를 호출하는 new 명령을 통해 s1이라는 객체를 생성하였다. s1 객체의 멤버에는 전역 변수로서 쓰일 m과 전역 함수 func이 있고, 이에 접근하기 위해 s1.m 등의 명령을 넣었다.

 

여기서는 s1이라는 객체를 생성하여 코드를 짰지만, 아래와 같이 객체 생성 없이도 코드를 작성할 수 있다.

 

code 2. static 멤버를 클래스 이름으로 접근 (객체 생성x)

// static 멤버가 속한 클래스의 이름으로 static 멤버에 접근
class ClassName {
	public static int m;
        public static void func(){
            m = 20;
        }
}

public class Main {
	public static void main(String[] args){
    	ClassName.m = 10; // m이 10
        ClassName.func(); // m이 20
    }
}

 

 

 

Non-Static과 Static 멤버 비교

  Non-static 멤버(인스턴스 멤버) Static 멤버(클래스 멤버)
공유의 특성 공유되지 않음. 동일한 클래스 내 객체들끼리 공유함.
공간적 특성 객체마다 멤버가 별도로 존재함. 멤버가 클래스 당 하나이며, 멤버는 객체 내부가 아닌 클래스 코드가 적재되는 별도의 공간에 생성됨.
-> 클래스 멤버라고 부름.
시간적 특성 객체 생성 시에 멤버가 생성됨. (함께)
-> 객체 생성 후 멤버를 사용할 수 있고, 객체가 사라지면 멤버도 사라짐.

클래스 로딩 시 생성, 즉 객체 생성 전에 멤버가 생성됨.
-> 객체 없어도 사용 가능(객체 유무와 무관), 객체가 사라져도 당연히 클래스 멤버는 그대로임.
프로그램 종료 시에 클래스 멤버 사라짐.

 

 

 

Static 멤버 주의점

클래스 멤버의 특징 중 세 번째인 시간적 특성에 집중해보자.

 

1. static 메소드는 non-static 멤버에 접근 불가

static 멤버 내의 static 메소드는 클래스 로딩 시에 생성되는데, 이는 인스턴스 멤버가 생성 되기 전 즉 객체가 생성되기 전일 것이다. 만약 이때 non-static 멤버의 객체가 생성되지도 않았는데 static 메소드를 실행하여 인스턴스 멤버에 접근한다면 당연히 오류가 생길 것이다. 따라서 이와 같은 문제로 static 메소드는 non-static 멤버에 접근할 수 없다. (물론 non-static 메소드가 static 멤버를 사용하는 건 가능하다! static 멤버 자체의 생성 및 사용은 객체 유무와 무관하기 때문이다.)

 

코드 예시를 살펴보자.

Class StaticEx {
    int n = 5;
    
    public static int m;
    public static void sFunc(){
    	n = 10; // 오류!
        iFunc(); // 오류!
    }
    
    public void iFunc(){
    	n = 10; // n은 10
    	m = 20; // m은 20
    }
}

 

 

2. static 메소드는 블럭 내에서 this 사용 불가

this는 현재 객체를 가리키는 레퍼런스이다. 그런데 만약 static 메소드가 객체가 생성되기 전에 호출되고, 이 메소드 블럭 내에서 this 명령어를 사용한다면 생성된 객체가 없어 오류가 생길 수 있을 것이다. 따라서 static 메소드는 this를 사용할 수 없다.

위 코드처럼 static 메소드는 블럭 내에서 this 사용 불가이지만, non-static 메소드는 static 멤버에 접근할 수 있기에 당연히 non-static 메소드 블록 내에서 this 명령으로 static 멤버에 접근하는 것도 가능하다.

 


 

static 멤버에 대해 정리해보며 멤버 자체의 정의와 static 멤버 특성들, static과 인스턴스 멤버의 서로 다른 생성 시점 그리고 이로 인한 static 메소드의 제약 조건들까지 복습해볼 수 있었다. 물론 실전으로 자바로 코드를 더 다양하게 작성하다보면 또 헷갈릴 수 있지만... 이 개념들을 차근차근히 머릿속에서 정리하여 기억해둔다면 코드를 짤 때도 큰 도움이 될 것 같다.