정적변수(static variable)
#
Find similar titles
- 최초 작성자
- 최근 업데이트
Table of Contents
static(정적 변수와 메소드) #
static은 보통 변수나 메소드 앞에 static을 붙여서 사용하게 된다.
static variable(정적 변수) #
예를 들어 다음과 같은 클래스가 있다고 한다.
public class Static_01 {
String lastname = "김";
public static void main(String[] args) {
HouseKim kms = new HouseKim();
HouseKim kks = new HouseKim();
}
}
김씨 집안을 나타내는 HouseKim클래스다. 위와 같은 클래스를 만들고 객체를 생성하면 객체마다 lastname을 저장하기 위한 메모리를 별도로 할당해야 한다. 하지만 가만히 생각해 보면 HouseKim클래스의 lastname은 어떤 객체이던지 동일한 값인 "김"을 얻어야한다. 이렇게 항상 변하지 않는 경우라면 static 사용 시 메모리의 이점을 얻는다.
다음은 변경된 예제이다.
public class HouseSeo {
static String lastname="서";
public static void main(String[] args){
HouseSeo sos = new HouseSeo();
HouseSeo ses = new HouseSeo();
}
}
위와 같이 lastname변수에 static 키워드를 붙이면 자바는 메모리 할당을 딱 한번만 하게 되어 메모리 사용에 이점을 볼 수 있다.
☆ 만약 HouseSeo클래스의 lastname값이 변경되지 않기를 바랄땐, static 키워드 앞에 final이라는 키워들 붙인다. final키워드느 한번 설정되면 그 값을 변경하지 못하게 하는 기능이 있다. 변경하려고 하면 예외가 발생한다.
static을 사용하는 또 한가지 이유로 공유의 개념을 들 수 있다. static으로 설정하면 단 한곳의 메모리 주소만 바라보기 때문에 static변수의 값을 공유하게 되는 것이다.
웹 사이트 방문시마다 조회수를 증가시키는 Counter프로그램이 있다고 하자.
public class Counter {
int count = 0;
Counter() {
this.count++;
System.out.println(this.count);
}
public static void main(String[] args) {
Counter c1 = new Counter();
Counter c2 = new Counter();
}
}
c1, c2 객체 생성시 count 값을 1씩 증가하더라도 c1과 c2의 count는 서로 다른 메모리를 가리키고 있기 때문에 원하던 결과가 나오지 않는다.
결과 값은 아래와 같다.
1
1
그렇다면 다음 예를 보자.
public class Counter {
static int count = 0;
Counter(){
this.count++;
System.out.println(this.count);
}
public static void main(String[] args){
Counter c1 = new Counter();
Counter c2 = new Counter();
}
}
int count=0 앞에 static 키워드가 추가되니 원하던데로 count값이 공유되어 다음과 같이 방문자수가 증가된 결과값이 나오게 된다.
1
2
보통 변수의 static 키워드는 프로그래밍 시 메모리의 효율보다는 두 번째처럼 값을 공유하기 위한 용도로 더 많이 사용된다.
static method #
static 이라는 키워드가 메소드 앞에 붙는다면, 이메소드는 클래스 메소드(static method)가 된다.
public class Counter{
static int count=0;
Counter(){
this.count++;
}
public static int getCount(){
return count;
}
public static void main(String[] args){
Counter c1 = new Counter();
Counter c2 = new Counter();
System.out.println(Counter.getCount());
}
}
getCount()라는 static 메소드가 추가되었다. main 메소드에서 getCount()메소드는 Counter.getCount()와 같이 클래스를 통해 호출 할 수 있게 된다.
※ 클래스 메소드 안에서는 인스턴스 변수 접근이 불가능 하다. 위 예에서 count는 static변수 이기 때문에 클래스 메서드에서 접근이 가능한 것이다.
보통 클래스 메소드는 유틸리티 성 메소드를 작성할 때 많이 사용된다. 예를 들어 " 오늘의 날짜 구하기", "숫자에 콤마 추가하기"등의 메서드를 작성할 때에는 클래스 메소드를 사용하는 것이 유리하다.
다음은 "날짜"를 구하는 Util클래스 예제이다.
import java.text.SimpleDateFormat;
import java.util.Date;
public class Util {
public static String getCurrentDate(String fmt) {
SimpleDateFormat sdf = new SimpleDateFormat(fmt);
return sdf.format(new Date());
}
public static void main(String[] args){
System.out.println(Util.getCurrentDate("yyyyMMdd"));
}
}
Util 클래스의 getCurrentDate라는 클래스 메소드를 이용하여 오늘의 날짜를 구하는 예제 이다. 위 클래스를 실행하면 오늘의 날짜가 출력될 것이다.
singleton pattern(싱글톤 패턴) #
디자인 패턴중 하나인 싱글톤에 대해서 알아보자. 싱글톤은 단 하나의 객체만을 생성하게 강제하는 패턴이다. 즉, 클래스를 통해 생성할 수 있는 객체는 한개만 되도록 만드는 것이 Singleton인 것이다.
class Singleton{
private Singleton(){
}
}
public class SingletonTest {
public static void main(Stringp[] arge){
Singleton singleton = new Singleton();
}
}
위와 같은 코드를 작성하면 컴파일 에러가 발생한다. 이유는, Singleton클래스의 생성자에 private키워드로 외부 클래스에서 Singleton클래스의 생성자로의 접근을 막았기 때문이다. 이렇게 생성자를 private로 만들어 버리면 외부 클래스에서 Singleton클래스를 new를 이용하여 생성 할 수 없게 된다.
new를 이용하여 무수히 많은 객체를 생성한다면 싱글톤의 정의에 어긋난다. 그래서 일단 new로 객체를 생성할 수 없도록 막은 것이다.
그렇다면 Singleton클래스의 객체는 어떻게 생성하는지 다음처럼 코드를 작성해 본다.
class Singleton {
private Singleton(){
}
public static Singleton getInstance(){
return new Singleton();
}
}
public class SingletonTest{
public static void main(String[] args){
Singleton singleton = Singleton.getInstance();
}
}
위와 같이 코드를 변경하면 이제 getInsatance라는 static 메소드를 이용하여 Singleton객체를 돌려 받을 수 있다. 하지만 getInstance를 호출 할 때마다 새로운 객체가 생성되게 된다. 그렇다면 Singleton이라고 할 수 없다. 그럼 다음과 같이 코드를 작성해 본다.
class Stingleton {
private static Singleton one;
private Singleton(){
}
public static Singleton getInstance(){
if(one==null){
one = new Singleton();
}
return one;
}
}
public class SingletonTest {
public static void main(String[] args){
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleont1 == singleton2);
}
}
Singleton클래스에 one이라는 static 변수를 두고 getInstance메소드에서 one값이 null인 경우만 객체를 생성하도록 하여 one객체가 단 한번만 만들어지도록 하였다.
getInsatance메소드의 동작원리를 살펴본다.
최초 getInstance가 호출되면 one이 null이므로 new에 의해 객체가 생성된다. 이렇게 한번 생성되면 one은 static 변수이기 때문에 그 이후로는 null이 아니게 된다. 그런 후에 다시 getInstance메소드가 호출되면 이제 one은 null이 아니므로 이미 만들어진 싱글톤 객체인 one을 항상 리턴하게 된다.
main메소드에서 getInstance를 두 번 호출하여 각각 얻은 객체가 같은 객체인지 조사해 보았다. 역시 예상대로 true가 출력되어 같은 객체임을 알 수 있었다.
static variable 주의사항 #
정적 변수는 위와 같은 장점들을 가지기에 언뜻 보면 사용하기 편하고 효율적으로 보일 수 있다. 특정 클래스에서 메서드의 호출 빈도가 매우 빈번하다면, 그때마다 인스턴스를 생성하는 것보다 더 나아 보일 수 있다.
하지만 사용에 대한 위험성 또한 존재하는데, 아래에 몇 가지 주의할 부분들을 살펴보자.
-
전체 프로그램과 같은 라이프라이클
정적으로 선언된 변수/메서드는 사용하던, 사용하지 않던 프로그램의 시작과 끝까지 메모리 내에 존재한다.
-
생성/소멸 통제 불가
프로그램이 로딩될 때 생성되고 종료되거나 JVM이 내려갈 때 소멸한다. 개발자가 의도적으로 생성/소멸에 관여할 수 없다.
-
Thread 불안정성
전역에서 사용되기 때문에 모든 thread는 static 필드를 공유한다. 이때 특정 thread에서 static 변수의 값을 변경하게 되면 다른 모든 스레드에서 영향을 받고, 이는 각 thread의 동기화 관련하여 문제를 일으킬 수 있다.
-
오버라이딩(overriding) 불가
정적으로 선언된 변수/메서드는 오버라이딩이 불가능하여 확장성이 제한된다.
-
직렬화(Serialization) 불가
객체의 직렬화는 인스턴스에 적용되기 때문에 클래스 자체 정보인 static 멤버는 대상에 포함되지 않는다.
-
메모리 문제
static 변수/메서드는 프로그램이 종료될 때까지 Garbage Collector로 회수되지 않기 때문에, static 필드나 메서드가 많아질수록 오히려 메모리에 악영향을 미칠 수 있다.
-
데이터 추적의 어려움
static 멤버는 전역으로 관리되므로 프로그램 전체에서 이 값에 접근할 수 있고 변경할 수 있다. 그 때문에 해당 데이터의 흐름을 추적하기 어려워져 테스트에 어려움을 겪을 수 있다.
올바르게 사용된다면 static 변수/메서드는 그 자체로 성능을 향상할 수 있다. 단순히 사용하기 편리하다는 이유로 무작정 선언하게 되면 예상하지 못한 문제를 일으킬 수 있으니, 역할을 이해하고 상황에 맞는 설계를 할 필요가 있다.