[Effective Java 3/e] 아이템 3 - private 생성자나 열거 타입으로 싱글턴임을 보증하라


아이템 3 - private 생성자나 열거 타입으로 싱글턴임을 보증하라

public static final 필드 방식의 싱글턴

public class Elvis {
  public static final Elvis INSTANCE = new Elvis();
  private Elvis() { ... }
}
  • 장점
    • 해당 클래스가 싱글턴임을 API에 명백히 드러남
    • 간결함
  • 단점
    • 리플렉션 공격에 취약
      • AccessibleObject.setAccessible 사용 시 private 생성자 호출 가능
      • 두 번째 객체가 생성 될 때 예외를 던져야 함
    • 직렬화 시 모든 인스턴스 필드를 transient 선언하고 readResolve 메서드 제공 해야 함
      • 그렇지 않으면 역직렬화시 새로운 인스턴스 만들어짐

정적 팩터리 방식의 싱글턴

public class Elvis {
  private static final Elvis INSTANCE = new Elvis();
  private Elvis() { ... }
  public static Elvis getInstance() { return INSTANCE; }
}
  • 장점
    • API 바꾸지 않고 싱글턴이 아니게 변경 가능
    • 정적 팩터리를 제네릭 싱글턴 팩터리로 만들 수 있다.
    • 정적 팩터리의 메서드 참조를 공급자(supplier)로 사용할 수 있다.
  • 단점
    • 리플렉션 공격에 취약
    • 직렬화 시 모든 인스턴스 필드를 transient 선언하고 readResolve 메서드 제공 해야 함
      • 그렇지 않으면 역직렬화시 새로운 인스턴스 만들어짐

열거 타입 방식의 싱글턴 - 바람직한 방법

public enum Elvis {
  INSTANCE;
}
  • 장점
    • 추가 노력 없이 직렬화 가능
    • 리플렉션 공격에 방어 가능
  • 단점
    • 만들려는 싱글턴이 Enum 외의 클래스를 상속해야 하면 이 방법을 사용할 수 없다.