💡static 키워드란?
Java에서 static 키워드는 클래스에서 공유되는 변수나 메서드를 정의할 때 사용하며, 메모리에 한번 할당되고 나면 프로그램이 종료될 때 해제된다.
static 변수를 남발하면 메모리 관리에 문제를 일으킬 수 있으므로 신중하게 사용해야 한다. 과도한 static 멤버의 사용은 프로그램의 성능을 저하시키고, 메모리 누수의 원인이 될 수 있다.
static 변수는 메모리에 고정적으로 할당되어 프로그램이 종료될 때 해제되는 변수이며, static 메서드는 객체 생성 없이 호출이 가능한 메서드라고 할 수 있다.
그리고 상태를 가지고 있지 않은 단순히 메서드만 가지고 있는 구조라고 볼 수 있는데, 이러한 이유로 인해 객체지향에 벗어난 개념이 되어 일부 좋지 않은 시선도 있는 편이다.
💡static 변수와 static 메서드의 특징
1. 스태틱 메모리 영역에 할당한다.
클래스가 로드될 때, 스태틱(static) 메모리 영역에 해당 변수나 메서드가 생성되며, 프로그램이 실행되는 동안 유지된다.
public class MyClass {
// 정적(static) 멤버 변수
public static int staticVariable = 10;
// 정적(static) 메서드
public static void staticMethod() {
System.out.println("This is a static method.");
}
}
정적 메모리 영역에 할당되는 정적 멤버는 프로그램이 시작될 때 클래스 로더에 의해 로드되어 메모리에 올라가며, 프로그램이 종료될 때까지 유지된다. 따라서 이 영역에 있는 데이터와 메서드는 객체의 생성과 관계없이 클래스 이름으로 직접 접근할 수 있다.
public class StaticTest {
public static void main(String[] args) {
// 클래스의 정적(static) 멤버에 접근
System.out.println("Static Variable: " + MyClass.staticVariable);
MyClass.staticMethod(); // 정적 메서드 호출
}
}
MyClass의 객체를 생성하지 않았음에도 불구하고, MyClass 클래스 이름으로 바로 호출하고 있다.
Static Variable: 10
This is a static method.
위 코드는 MyClass 클래스의 정적(static) 멤버인 staticVariable과 staticMethod()에 접근한다.
프로그램이 시작될 때, MyClass 클래스가 로드되면서 정적 메모리 영역에 staticVariable과 staticMethod()가 할당되며, StaticTest 클래스에서 이들에 접근하여 값을 출력하고 정적 메서드를 호출한다.
2. 클래스 이름으로 직접 접근한다.
앞서 MyClass 클래스의 인스턴스를 생성하지 않고도 호출하여 사용한 것 처럼 클래스 이름으로 직접 접근하여 사용할 수 있다.
인스턴스를 생성하지 않고도 사용할 수 있다는 점에 대해서 추가 설명을 하자면 객체를 생성하지 않고도 해당 메소드를 사용할 수 있다는 것을 의미한다.
정적(static) 메소드는 객체가 생성되지 않은 상태에서도 클래스 이름을 통해 직접 호출할 수 있다. 즉, 해당 메서드가 클래스에서 독립적으로 작동하며, 특정한 객체의 인스턴스에 종속되지 않는 것이다.
public class Test {
// 정적(static) 메서드 정의
public static void sm() {
System.out.println("this is static method!");
}
// 인스턴스(non-static) 메서드 정의
public void m() {
System.out.println("this is non-static method!");
}
}
public class StaticTest {
public static void main(String[] args) {
// Test 클래스의 정적(static) 메서드 호출
Test.sm(); // O
// Test 클래스의 인스턴스(non-static) 메서드 호출은 컴파일 오류 발생
// Test.m(); // X
// Test 클래스의 인스턴스 생성
Test test = new Test();
// 생성된 Test 인스턴스의 정적(static) 메서드 호출은 권장되지 않음
test.sm(); // X
// 생성된 Test 인스턴스의 인스턴스(non-static) 메서드 호출
test.m(); // O
}
}
StaticTest 클래스에는 두 가지 메서드가 있다.
sm()는 정적(static) 메서드로, 인스턴스 생성 없이 클래스 이름으로 직접 호출할 수 있으며, "this is static method!"를 출력한다.
m()는 인스턴스(non-static) 메서드로, 인스턴스를 생성한 후에만 호출할 수 있으며, "this is non-static method!"를 출력한다.
인스턴스
Exam ex = new Exam();
해당 클래스의 구조로 컴퓨터 저장공간에서 할당된 실체를 의미한다.
3. 클래스의 모든 인스턴스에서 공유된다.
동일한 클래스의 모든 인스턴스에서 공유되어 사용된다. 따라서 한 인스턴스에서 static 변수의 값을 변경하면 다른 인스턴스에서도 해당 값이 변경된다.
public class SharedVariableExample {
// 정적(static) 멤버 변수
public static int sharedVariable = 0;
// 인스턴스 메서드: static 변수를 증가시키는 역할
public void increaseSharedVariable() {
sharedVariable++; // static 변수 증가
}
// 인스턴스 메서드: static 변수의 값을 반환하는 역할
public int getSharedVariable() {
return sharedVariable;
}
public static void main(String[] args) {
// 첫 번째 인스턴스 생성
SharedVariableExample instance1 = new SharedVariableExample();
// 두 번째 인스턴스 생성
SharedVariableExample instance2 = new SharedVariableExample();
// 인스턴스 1에서 static 변수 값을 증가
instance1.increaseSharedVariable();
// 인스턴스 1에서 static 변수 값 확인
System.out.println("Instance 1: " + instance1.getSharedVariable()); // 출력: 1
// 인스턴스 2에서 static 변수 값 확인
System.out.println("Instance 2: " + instance2.getSharedVariable()); // 출력: 1 (동일한 값이 출력됨)
}
}
Instance 1: 1
Instance 2: 1
위 코드에서는 SharedVariableExample 클래스의 sharedVariable이라는 static 변수를 두 인스턴스에서 공유하고 있다. 즉, 한 인스턴스에서 sharedVariable 값을 변경하면, 다른 인스턴스에서도 해당 변경된 값을 공유한다.
첫 번째 인스턴스에서 sharedVariable 값을 1 증가시켰으므로, 첫 번째 출력에서는 Instance 1: 1이 출력되며, 두 번째 인스턴스에서도 같은 static 변수를 공유하고 있으므로, 두 번째 출력에서도 값이 1로 유지된다.
4. 클래스당 하나만 생성된다.
클래스당 하나만 생성되며, 이를 클래스 멤버라고도 부른다. 따라서 모든 인스턴스에서 동일한 값을 참조한다.
public class Counter {
// 정적(static) 변수
public static int count = 0;
// 증가 메서드
public void increment() {
count++;
}
// 값을 가져오는 메서드
public int getCount() {
return count;
}
}
이 클래스에서 count 변수는 정적(static) 변수로 선언되어 있다. 따라서 모든 Counter 클래스의 인스턴스가 공유하는 변수이다.
public class Main {
public static void main(String[] args) {
// Counter 클래스의 인스턴스 생성
Counter counter1 = new Counter();
Counter counter2 = new Counter();
// 각 인스턴스에서 count 값 증가
counter1.increment();
counter2.increment();
// 두 인스턴스에서 count 값을 가져옴
System.out.println("Counter 1: " + counter1.getCount()); // Counter 1: 2
System.out.println("Counter 2: " + counter2.getCount()); // Counter 2: 2
}
}
위 코드에서 counter1과 counter2 인스턴스는 동일한 클래스를 기반으로 만들어졌으므로 정적 변수인 count를 공유한다. 그 결과, 각 인스턴스에서 increment() 메서드를 호출하여 count 값을 증가시키면 모든 인스턴스에서 동일한 값을 참조하므로, 출력된 결과는 모두 2가 된다.
5. static 멤버만 접근할 수 있다.
static 메서드 안에서는 static 멤버들만 직접적으로 접근할 수 있고, 인스턴스 멤버는 사용할 수 없다.
public class Counter {
// 정적(static) 변수
public static int staticCount = 0;
// 인스턴스 변수
public int instanceCount = 0;
// 정적(static) 메서드
public static void staticMethod() {
System.out.println("This is a static method.");
// static 메서드 안에서는 static 변수에만 접근 가능
System.out.println("Static count: " + staticCount);
// 아래 주석 처리된 코드는 오류 발생
// System.out.println("Instance count: " + instanceCount);
}
// 인스턴스 메서드
public void instanceMethod() {
System.out.println("This is an instance method.");
// 인스턴스 메서드 안에서는 static 변수와 인스턴스 변수에 모두 접근 가능
System.out.println("Static count: " + staticCount);
System.out.println("Instance count: " + instanceCount);
}
}
위 코드에서 staticMethod()는 정적(static) 메서드이며, instanceMethod()는 인스턴스 메서드이다.
정적 메서드인 staticMethod() 안에서는 staticCount 변수에만 접근할 수 있고, 인스턴스 변수인 instanceCount에는 접근할 수 없다. 반면에 인스턴스 메서드인 instanceMethod() 안에서는 staticCount와 instanceCount 모두에 접근할 수 있다.
public class Main {
public static void main(String[] args) {
// Counter 클래스의 인스턴스 생성
Counter counter = new Counter();
// 정적(static) 메서드 호출
Counter.staticMethod();
// 인스턴스 메서드 호출
counter.instanceMethod();
}
}
This is a static method.
Static count: 0
This is an instance method.
Static count: 0
Instance count: 0
결론적으로 staticMethod()에서는 staticCount에만 접근 가능하고, instanceMethod()에서는 staticCount와 instanceCount 모두에 접근할 수 있다.
💡static 변수와 static 메서드의 사용 목적
1. 전역 변수 및 함수 생성
모든 클래스에서 호출 가능한 전역 변수나 전역 함수를 만들기 위해 사용된다. 이를 통해 프로그램 전반에 걸쳐 일관된 데이터나 기능을 제공할 수 있다.
2. 공유 데이터 관리
static 멤버는 클래스당 하나만 생성되어 클래스의 모든 인스턴스에서 공유된다. 따라서 여러 인스턴스 간에 공유해야 하는 데이터를 관리할 때 사용된다.
💡static 변수와 static 메서드의 활용
class Counter {
static int count = 0; // 정적(static) 변수
Counter() {
count++; // count 증가
}
// 정적(static) 메서드
public static int getCount() {
return count; // count 값 반환
}
}
public class Main {
public static void main(String[] args) {
// Counter 클래스의 객체 생성
Counter c1 = new Counter(); // 첫 번째 객체 생성
Counter c2 = new Counter(); // 두 번째 객체 생성
// 정적(static) 메서드 호출하여 count 값 출력
System.out.println("Counter 값: " + Counter.getCount());
// c1 객체의 count 값을 변경
c1.count = 10;
// c2 객체의 count 값도 변경되어 있음을 확인
System.out.println("변경된 Counter 값: " + c2.count);
}
}
위의 예제는 Counter 클래스를 사용하여 객체를 생성할 때마다 count 값을 증가시키는 예제이다.
Counter 클래스의 count 변수는 static으로 선언되어 있어 모든 Counter 객체가 해당 변수를 공유하므로 객체를 생성할 때마다 count 값이 증가한다.
메인 메서드에서는 두 개의 Counter 객체를 생성한 후, getCount() 메서드를 사용하여 count 값을 출력한다. 그리고 static 변수가 모든 객체에서 공유되기 때문에 c1 객체의 count 값을 변경한 후, c2 객체의 count 값도 함께 변경되어 있음을 확인할 수 있다.
참고 자료
스태틱, 점프 투 자바, 2023.08.01.
[Java] static변수와 static 메소드, 망나니개발자, 2019.02.04.
[Java] 정적 메소드(static Method)는 언제 사용할까?, coco3o, 2022.06.11.