싱글턴: 인스턴스를 오직 하나만 생성할 수 있는 클래스
싱글턴의 단점: 클라이언트 테스트가 어려울 수 있음 - mock 객체 구현이 어렵기 때문에(인터페이스 만들고... mock 싱글턴으로 만들고...)
크게 두가지 방식이 있는데, 이는 모두 private 생성자와 유일한 접근 수단인 public static 멤버를 사용한다.
public static final
맨 처음에 만들면 그대로 하나의 인스턴스를 유지시킬 수 있음
클라이언트는 권한을 갖고 리플렉션 API를 사용하여 private 생성자를 호출하는 방법을 제외하고는 추가적인 객체를 생성할 수 없음
위 방법 역시 생성자에서 두번째 호출이 되면 예외를 발생시켜 보완할 수 있음
장점
해당 클래스가 싱글턴임이 API에 명백히 드러남
간결함
정적 팩터리 메서드를 public static 멤버로 제공
맨 처음에 private static final 인스턴스를 만들어 할당해 놓음
이후 정적 팩터리 메서드를 통해 만들어둔 인스턴스를 반환
위 리플렉션 방법에 대한 예외처리는 동일하게 필요함
장점
API를 바꾸지 않아도 싱글턴이 아니게 변경할 수 있음
제네릭 싱글턴 팩터리로 만들 수 있음 (이후에 다룸)
정적 팩터리의 메서드 참조를 공급자로 변경할 수 있음 EX) Elvis::getInstane 를 Supplier<Elvis>로 사용하는 것
위 장점이 필요 없다면 public 멤버 객체를 사용하는 것이 좋음
원소가 하나인 열거 타입 선언
위 두가지 방법의 문제점인 클래스의 직렬화
의 복잡함 문제와 리플렉션 공격에서 제 2의 클래스 생성을 완벽히 막아줌
싱글턴 클래스를 생성하는 가장 좋은 방법
단, Enum 외 클래스를 상속해야한다면 사용할 수 없음 (인터페이스를 구현할 수는 있음)
추가.
싱글턴 클래스 직렬화
단순 Serializable 을 구현하는 것으로는 부족함
모든 인스턴스 필드를 일시적(transient)이라고 선언해야함
readResolve 메서드를 제공해야함
위 방법을 사용하지 않으면 직렬화된 인스턴스를 역직렬화 시, 새로운 인스턴스가 만들어져 싱글턴을 보장할 수 없음.