[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 외의 클래스를 상속해야 하면 이 방법을 사용할 수 없다.