싱글턴이란 인스턴스를 오직 하나만 생성할 수 있는 클래스를 말한다. 싱글턴의 전형적인 예로는 함수와 같은 무상태 객체, 설계상 유일한 시스템 컴포넌트를 말한다. 하지만 싱글턴 클래스는 타입을 인터페이스로 정의하고 그것을 구현체로 정의한 것이 아니라면 테스트하기 어려워질 수 있다.
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() {
...
}
public void leaveTheBuilding() {
System.out.println("elvis");
}
}
private 생성자는 public static final 필드인 Elvis.INSTANCE를 초기화할 때 딱 한번만 호출 된다. 예외는 단 한가지, 권한이 있는 클라이언트는 리플렉션 API인 AccessibleObject.setAccessible을 사용해 private 생성자를 호출할 수 있는데, 이렇나 공격을 방어하려면 생성자를 수정하여 두번째 객체가 생성되려 할 때 예외를 던져 막을 수 있다.
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() {
...
}
public static Elvis getInstance() {
return INSTANCE;
}
public void leaveTheBuilding() {
System.out.println("elvis");
}
}
Elvis.getInstance는 항상 같은 객체의 참조를 반환하므로 Elvis의 유일한 인스턴스임을 보장한다.
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() {
System.out.println("elvis");
}
}
가장 바람직한 방식은 원소가 하나뿐인 열거 타입이 싱글턴을 만드는 것이 가장 좋은 방법이다.
public 필드 방식과 비슷하지만 간결하고, 직렬화 상황이나 리플렉션 공격에도 방어가 가능하다.
단, 만드려는 싱글턴이 Enum외의 클래스를 상속해야 한다면 이방법은 사용할 수 없다.
첫 번째, 두 번째로 만든 싱글턴 클래스를 직렬화 하려면 단순히 Serializable을 구현하는 것 외, 인스턴스 필드를 일시적 이라고 선언하고, readResolve 메서드를 제공해야만 한다.
private Object readResolve throws ObjectStreamException {
return INSTANCE;
}