어플리케이션이 실행될 때, 어떤 클래스가 한번만 메모리에 할당하고, 그 메모리에 인스턴스를 만들어 사용하는 디자인 패턴이다.
다시말해, 객체의 인스턴스가 오직 1개만 생성되는 패턴을 의미한다.
싱글톤의 가장 일반적인 생성 방식은 두 가지가 있다.
두 방식 모두 생성자는 private 로 감춰두고,
인스턴스에 접근할 수 있는 유일한 수단으로 public static 맴버를 하나 마련해 둔다.
public class Singleton1{
// public static final 맴버
public static final Singleton1 s1 = new Singleton1();
// private 생성자
private Singleton1() { }
public void doSomething1() { }
}
실행문
public class MainSingleton {
public static void main(String[] args) {
// public static 맴버가 final 필드인 방식의 Singleton
Singleton1 chk1_s1 = Singleton1.s1;
Singleton1 chk2_s1 = Singleton1.s1;
// 둘이 주소값이 같은지 확인
System.out.println(chk1_s1 == chk2_s1);
}
}
결과
true
public class Singleton2 {
// public static final 맴버
public static final Singleton2 s2 = new Singleton2();
// private 생성자
private Singleton2() { }
// 정적 팩토리 메서드
public static Singleton2 getInstance(){ return s2; }
public void doSomething2() { }
}
실행문
public class MainSingleton {
public static void main(String[] args) {
// 정적 팩터리 메서드를 public static 멤버로 제공하는 방식의 Singleton
Singleton2 chk1_s2 = Singleton2.getInstance();
Singleton2 chk2_s2 = Singleton2.getInstance();
// 둘이 주소값이 같은지 확인
System.out.println(chk1_s2 == chk2_s2);
}
}
결과
true
하지만!!!
이 두 방식에는 치명적인 단점이 존재한다.
만약 이 방법들을 직렬화에 사용한다면, 역직력화 할 때 새로운 인스턴스가 생겨서 싱글턴이 아니게 되어 버린다.
이것을 해결하려면 모든 인스턴스 필드를 직렬화 대상에서 제외(transient)한다 선언하고, readResolve 메서드를 제공해야 한다.