
Java로 개발하다 보면 public static void main() 같은 구문에서 자주 static 키워드를 마주하게 됩니다.
하지만 정작 static이 무엇을 의미하는지, 언제 사용해야 하는지, 그리고 어떤 위험이 있는지 헷갈릴 때가 많습니다.
이 글에서는 다음 내용을 중심으로 static을 쉽게 이해할 수 있도록 정리했습니다.
- static 키워드가 의미하는 것
- static 변수와 메서드의 동작 방식
- static을 사용하기 좋은 상황과 피해야 할 상황
- 대표적인 활용 예(유틸리티 클래스, 상수, 싱글톤 등)
- static의 장단점과 사용 시 주의할 점
이 글을 읽고 나면 static을 보다 의도적이고 안전하게, 그리고 객체지향 관점에서 적절하게 활용할 수 있게 될 거에요!
static이란 무엇인가?

static은 클래스 수준에서 관리되는 멤버(변수/메서드)를 정의하는 키워드입니다.
- 인스턴스 멤버: 객체가 생성될 때 힙(Heap)에 만들어지며, 각 객체마다 독립적으로 존재합니다.
- static 멤버: 클래스가 로드되는 시점에 메서드 영역(Method Area)에 생성되며, 모든 인스턴스가 함께 공유합니다.
즉, static은 객체에 속하지 않는 멤버를 의미합니다.
그래서 객체를 따로 생성하지 않아도 접근할 수 있고, 여러 인스턴스가 하나의 값이나 동작을 공유하도록 만드는 데 유용합니다.
static 변수란?
static 변수(정적 변수, 클래스 변수)는 클래스의 모든 인스턴스가 함께 공유하는 변수입니다. 인스턴스를 몇 개 생성하든, 이 변수는 하나만 존재합니다.
예제
public class Counter {
private static int count = 0;
public Counter() {
count++;
}
public int getCount() {
return count;
}
}
public class Main {
public static void main(String[] args) {
Counter c1 = new Counter();
Counter c2 = new Counter();
Counter c3 = new Counter();
System.out.println(c1.getCount()); // 3
System.out.println(c2.getCount()); // 3
System.out.println(c3.getCount()); // 3
}
}
위 코드에서 count는 static 변수이기 때문에 객체가 새로 만들어질 때마다 따로 생성되지 않습니다.
하나의 값만 유지되며, 이 값을 통해 몇 개의 인스턴스가 생성되었는지 쉽게 추적할 수 있습니다.
static 변수를 사용하는 이유
- 여러 인스턴스가 공유해야 하는 값이 있을 때
- 공용 설정 값, 생성된 인스턴스 수, 캐싱된 데이터처럼 클래스 단위로 관리하는 데이터가 필요할 때
즉, 하나만 존재해야 하는 값을 다루고 싶다면 static 변수가 자연스러운 선택이 됩니다.
static 메서드란?
static 메서드는 인스턴스를 만들지 않고도 바로 호출할 수 있는 메서드입니다.
객체의 상태와 무관하게 동작하는 기능을 정의할 때 주로 사용합니다.
예제
public class MathUtils {
public static int add(int a, int b) {
return a + b;
}
public static int subtract(int a, int b) {
return a - b;
}
}
public class Main {
public static void main(String[] args) {
int sum = MathUtils.add(5, 3);
int diff = MathUtils.subtract(5, 3);
System.out.println(sum); // 8
System.out.println(diff); // 2
}
}
MathUtils는 객체를 만들 이유가 없는 단순 연산을 제공하므로, 메서드를 static으로 정의하면 더욱 명확하고 편리합니다.
static 메서드를 사용하기 좋은 상황
- 인스턴스의 상태와 무관하게 동작하는 기능을 제공할 때
- 문자열 처리, 수학 계산 등 유틸리티성 기능을 모아둔 클래스에서
- 여러 곳에서 쉽게 호출해야 하는 공통 기능을 제공할 때
즉, 메서드가 객체 내부 상태를 참조할 필요가 없다면 static 메서드가 자연스러운 선택입니다.
static의 대표적인 활용 사례
static은 객체 생성과 무관하게 동작할 수 있다는 특성 덕분에 여러 상황에서 유용하게 쓰입니다. 대표적인 활용 패턴은 다음과 같습니다.
1) 유틸리티 클래스
상태가 없고, 불변이며, 공통적으로 사용할 기능을 모아둘 때 적합합니다.
public class StringUtils {
public static String reverse(String str) {
return new StringBuilder(str).reverse().toString();
}
}
객체를 만들 이유가 없으므로 모든 메서드를 static으로 제공하는 방식이 자연스럽습니다.
2) 상수 정의
프로그램 전반에서 사용되는 값을 한곳에서 관리하고 싶을 때 사용합니다.
public class Constants {
public static final double PI = 3.141592653589793;
}
static으로 모든 곳에서 동일하게 접근할 수 있고, final로 값의 변경을 막아 안정성을 확보할 수 있습니다.
3) 싱글톤 패턴 구현
static을 활용하면 하나의 인스턴스만 존재하도록 제어할 수 있습니다.
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
instance를 static으로 선언해 클래스 차원에서 하나의 객체만 유지하도록 보장합니다.
static의 단점과 주의사항
static은 편리하지만, 잘못 사용하면 유지보수성과 안정성을 해칠 수 있습니다. \
다음과 같은 단점과 주의점을 함께 고려해야 합니다.
1) 객체지향 원칙 위반 가능성
static은 객체의 상태가 아니라 전역 상태를 공유합니다.
이로 인해 캡슐화가 약해지고, 코드가 객체지향 설계에서 멀어질 수 있습니다.
2) 메모리 누수 위험
static 멤버는 JVM이 종료될 때까지 메모리에 유지됩니다.
참조가 계속 쌓이면 의도치 않게 메모리 누수가 발생할 수 있습니다.
public class MemoryLeak {
private static List<String> dataList = new ArrayList<>();
}
전역 리스트처럼 해제되지 않는 구조는 특히 주의가 필요합니다.
3) 동시성 문제
여러 스레드가 동시에 static 멤버에 접근하면 Race Condition이 발생할 수 있습니다.
동기화로 해결할 수 있지만 성능 저하를 동반할 수 있습니다.
public static synchronized void increment() {
count++;
}
4) 테스트 어려움
static 메서드는 모킹하거나 대체하기 어렵기 때문에
테스트 코드의 유연성이 떨어지고, 의존성 주입(DI) 같은 패턴도 활용하기 힘듭니다.
5) 유연성·확장성 저하
static은 다형성을 적용할 수 없고, 환경에 따라 값을 바꾸기도 어렵습니다.
결과적으로 구조적 확장이나 리팩터링을 방해할 수 있습니다.
static을 사용하기 전에 아래 질문에 모두 YES라고 답할 수 있어야 합니다.
- ✔ 이 값이나 기능이 클래스 수준에서 공유되어야 하는가?
- ✔ 상태가 변해도 안전한가? (Immutable하거나 Thread-safe한가?)
- ✔ static을 사용해도 테스트가 어려워지지 않는가?
- ✔ 향후 기능 추가나 변경 시 확장에 문제가 없을까?
- ✔ 객체지향 구조를 해치지 않는가?
static은 매우 편리한 도구지만, 편리하다는 이유로 남용하면 코드 품질이 빠르게 떨어질 수 있습니다.
필요한 경우에만, 의도를 명확히 하여 사용하는 것이 가장 중요합니다.
static은 Java에서 매우 강력하고 자주 사용되는 키워드입니다.
올바르게 활용하면 코드가 더 효율적이고 직관적으로 변하지만, 남용하면 객체지향 구조를 해치고 유지보수를 어렵게 만들 수 있습니다.
따라서 static을 사용할 때는 다음 질문을 항상 떠올리면 좋습니다.
- 정말 공유되어야 하는 데이터인가?
- 정말 인스턴스 상태와 무관한 기능인가?
- 테스트, 확장성, 동시성 측면에서 문제가 없을까?
static을 의도적으로, 필요할 때만 적절하게 사용하는 개발자가 결국 객체지향을 제대로 이해하는 개발자입니다.
'Language > Java ☕️' 카테고리의 다른 글
| 자바 메모리 구조 이해하기 (0) | 2024.05.22 |
|---|---|
| 자바의 가비지 컬렉션(GC) 한 번에 이해하기 (0) | 2024.02.02 |
| String vs StringBuffer vs StringBuilder — 자바 문자열 클래스의 차이 완전 정리 (0) | 2024.02.02 |
| Double과 Float를 사용하면 안되는 이유 (0) | 2024.02.01 |
| JDK, JRE, JVM의 차이와 자바 실행 구조 이해하기 (0) | 2024.02.01 |
안녕하세요, 저는 주니어 개발자 박석희 입니다. 언제든 하단 연락처로 연락주세요 😆