'정적인', '고정적인' 이라는 사전적 의미를 가진다.
이를 Java 관점으로 해석하면, '클래스의' 또는 '공통적인' 이라는 의미를 가진다.
Static을 이해하기 위해서는 먼저, JVM(자바 가상 머신)을 이해할 필요가 있다.
쉽게 말하면, "자바를 실행하기 위한 가상의 컴퓨터"라고 할 수 있다.
자바 애플리케이션이 실행되면 JVM은 시스템으로부터 필요한 메모리를 할당받고, 용도에 따라 여러 영역으로 나누어 관리한다. Method Area, Call Stack, Heap이 대표적이다.
자바에서 static 키워드는 클래스 레벨의 변수나 메서드, 블록을 정의할 때 사용된다.
왜냐하면, static 멤버는 클래스가 로드될 때 메모리의 메서드 영역에 할당되기 때문이다. 이는 모든 인스턴스가 공유하는 특성 때문에 유용하게 사용될 수 있다.
예를 들어, 어떤 클래스의 인스턴스들이 공통적으로 사용해야 하는 값을 static변수로 선언할 수 있다.
static 키워드의 남용은 객체 지향 프로그래밍의 원칙과 상반되며, 메모리 사용량 증가로 이어질 수 있다. 따라서, static 멤버의 사용은 신중하게 결정해야 한다.
👉 static 멤버는 프로그램 종료 시 까지 메모리에 남아 있어, 과도한 static 사용은 메모리 누수의 원인이 될 수 있기 때문이다. 이는 특히, 대규모 애플리케이션에서 성능 저하의 원인이 될 수 있다.
static 변수의 설명을 보고 처음 든 생각은 "전역 변수 아니야? 전역 변수랑 다른게 뭐지?" 였다.
바로 본론부터 말하자면, 자바에는 전역 변수라는 개념이 없다.
C, Python에서 처럼 특정 클래스에 속하지 않는 전역 변수의 개념은, 모든 변수가 클래스 안에서 정의되어야 하고, 특정 클래스에 종속되지 않은 변수를 선언할 방법이 없는 자바와는 반대되는 개념이기 때문이다.
만약 자바에서 정적 변수를 전역 변수처럼 사용해야 한다면, 싱글톤 패턴(Singleton Pattern)이나 private static변수와 getter/setter를 활용하는 방법이 있다.
(Static 변수) 예시 코드
// static 변수 (클래스 변수)
public class MyMathStaticBasic {
public static final String DESCRIPTION = "static 변수";
}
// 인스턴스 변수
public class MyMathBasic {
public long a;
public long b;
public String description = "인스턴스 변수";
}
테스트
@Test
@DisplayName("static 변수, 인스턴스 변수")
public void staticStr() {
// static 변수는 인스턴스를 생성하지 않아도 사용할 수 있다.
System.out.println("static 변수 출력 = " + MyMathStaticBasic.DESCRIPTION);
// 인스턴스 변수는 인스턴스를 생성해야 사용할 수 있다.
MyMathBasic mathBasic = new MyMathBasic();
System.out.println("인스턴스 변수 출력 = " + mathBasic.description);
}
결과
static 변수
인스턴스 변수 출력 = 인스턴스 변수
(Static 메서드) 예시 코드
// static 변수, static 메서드 (클래스 변수, 클래스 메서드)
public class MyMathStaticBasic {
public static final String DESCRIPTION = "static 변수";
/** 매개변수로 가능 */
public static long add(long a, long b) {
return a + b;
}
public static long substract(long a, long b) {
return a - b;
}
public static long multiply(long a, long b) {
return a * b;
}
public static long divide(long a, long b) {
return a / b;
}
}
// 인스턴스 변수
public class MyMathBasic {
long a;
long b;
String description = "인스턴스 변수";
/** 인스턴스 변수 a, b만 이용하므로 매개변수가 필요 없다. */
public long add() {
return a + b;
}
public long substract() {
return a - b;
}
public long multiply() {
return a * b;
}
public long divide() {
return a / b;
}
}
테스트
@Test
@DisplayName("static 메소드")
public void mathStatic() {
long num1 = 100L;
long num2 = 10L;
System.out.println("add(num1, num2) = " + MyMathStaticBasic.add(num1, num2));
System.out.println("subtract(num1, num2) = " + MyMathStaticBasic.subtract(num1, num2));
System.out.println("multiply(num1, num2) = " + MyMathStaticBasic.multiply(num1, num2));
System.out.println("divide(num1, num2) = " + MyMathStaticBasic.divide(num1, num2));
assertEquals(110L, MyMathStaticBasic.add(num1, num2));
assertEquals(90L, MyMathStaticBasic.subtract(num1, num2));
assertEquals(1000L, MyMathStaticBasic.multiply(num1, num2));
assertEquals(10.0, MyMathStaticBasic.divide(num1, num2));
}
@Test
@DisplayName("인스턴스 메소드")
public void mathBasic() {
MyMathBasic mathBasic = new MyMathBasic();
mathBasic.a = 200L;
mathBasic.b = 10L;
System.out.println("add() = " + mathBasic.add());
System.out.println("subtract() = " + mathBasic.subtract());
System.out.println("multiply() = " + mathBasic.multiply());
System.out.println("divide() = " + mathBasic.divide());
assertEquals(210L, mathBasic.add());
assertEquals(190L, mathBasic.subtract());
assertEquals(2000L, mathBasic.multiply());
assertEquals(20.0, mathBasic.divide());
}
결과 (static 메서드)
add(num1, num2) = 110
subtract(num1, num2) = 90
multiply(num1, num2) = 1000
divide(num1, num2) = 10.0
결과 (인스턴스 메서드)
add() = 210
subtract() = 190
multiply() = 2000
divide() = 20.0
(Static 블록) 예시 코드
public class AppConfig {
static {
// 초기화 코드
}
}
자바에서 static 키워드는 유용하지만, 그 사용은 신중해야 한다.
static 멤버의 남용은 객체 지향 프로그래밍 원칙에 어긋난다. 또한, 프로그램의 생명주기 동안 메모리에 상주하므로 불필요한 메모리 사용을 초래할 수 있기 때문에, 메모리 관리 측면에서도 부정적인 영향을 줄 수 있다. 따라서, 필요한 경우에만 제한적으로 이루어져야 한다.
적절한 경우에 static을 사용하면 코드의 재사용성과 효율성, 안정성을 높일 수 있다. 객체 지향의 원칙과 메모리 관리를 고려하여, static을 올바르게 사용하자.
[참고]
https://gymdev.tistory.com/73
https://f-lab.kr/insight/understanding-and-using-java-static-keyword?gad_source=2&gad_source=1&gclid=Cj0KCQiAq-u9BhCjARIsANLj-s2gaTT9B1lur1FTJPIfzaSaWpayPS9CS_9tjJvPEzyDAIUEJ9GWuzcaAhuuEALw_wcB