[Item 3] private 생성자나 열거 타입으로 싱글턴임을 보증하라.

손현경 (보름)·2023년 6월 27일

effective-java

목록 보기
3/4

싱글턴

오직 하나의 인스턴스만 생성할 수 있는 클래스 입니다.

  • 무상태 객체 → 아이템 24에서 더 자세히 다루겠습니다.
  • 시스템 컴포넌트 (유일해야 한다)
    등이 있습니다.

싱글턴을 만드는 방식

1. public static final

public class Student {

  public static final Student STUDENT = new Student();

  private Student() {
  }
}
  • 생성자는 private으로 선언되어 있어서, 외부에서 접근할 수 없습니다.
    • STUDENT 객체를 생성할 때 딱 한번 호출됩니다.
  • public static final로 선언한 객체만이 유일하게 외부에서 접근할 수 있습니다.
  • API에 싱글턴임이 명확히 드러납니다.

단, 이 방식에는 취약점이 있다.

public class Student {

  public static final Student STUDENT = new Student();

  private Student() {
  }

  public static void main(String[] args) {
    try {
      Constructor<Student> constructor = Student.class.getDeclaredConstructor();
      constructor.setAccessible(true);
      Student instance = constructor.newInstance(); // private 생성자를 호출하여 객체를 생성합니다.
      System.out.println(instance);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

리플렉션 API 인 AccessibleObject.setAccessible() 메서드로 private 생성자를 호출 할 수 있다.

private static int instanceCount = 0;

private Student() {
  if (instanceCount > 0) {
    throw new IllegalStateException("생성자는 한번만 호출될 수 있습니다.");
  }
  instanceCount++;
}

이 경우에는, 생성자에서 두번째 객체가 생성되려 할 때 예외를 던지면 됩니다.

2. 정적 팩터리 방식

public class SecondSingleton {

  private static final SecondSingleton INSTANCE = new SecondSingleton();

  private SecondSingleton() {
  }

  public static SecondSingleton getInstance() {
    return INSTANCE;
  }
}
  • public static final 방식에서는 필드를 직접 가져왔는데, 정적 팩터리 방식은 그 필드를 getInstance(정적 팩터리 메서드)로 가져옵니다.
  • API를 변경하지 않아도, 싱글턴이 아니게 만들 수 있습니다. (스레드별로 다른 인스턴스를 반환)
  • 정적 팩터리를 제네릭 싱글턴 팩터리로 만들 수 있습니다. → 아이템 30에서 자세히 다루겠습니다.
  • 정적 팩터리의 메서드 참조를 공급자로 사용할 수 있습니다. → 아이템 43, 44에서 자세히 다루겠습니다.

가짜 객체의 탄생을 예방하려면

  • 모든 인스턴스를 transient라고 선언하고, readResolve 메서드(아이템 89에서 더 자세히 다루겠습니다.)를 제공해야 합니다.
  • 그렇지 않으면, 인스턴스를 역직렬화 할 때마다 새로운 인스턴스가 만들어집니다.

3. 열거(Enum) 타입 - best

public enum EnumSingleton {
  INSTANCE
}
  • 앞의 방법들에 비해서 간결합니다.
  • 추가적인 노력 없이 직렬화 문제 & 리플렉션 공격에 의한 가짜 객체 생성을 막을 수 있습니다.
  • 대부분의 상황에서는 열거 타입이 싱글턴을 만드는 가장 좋은 방법입니다.
profile
빛나는 개발자가 되는 그날까지...

1개의 댓글

comment-user-thumbnail
2023년 6월 27일

item 24, 30, 43, 44, 89

답글 달기