인스턴스를 오직 하나만 생성할 수 있는 클래스
함수와 같은 무상태(stateless) 객체
설계상 유일해야 하는 시스템 컴포넌트
싱글톤 클래스는 이를 사용하는 클라이언트를 테스트하기 어렵게 만들 수 있음
because 생성자를 private로 감춰 두었기 때문에 mock 구현으로 대체가 어렵기 때문
코드가 싱글턴임이 API에 명백하게 드러난다
public class Elvis {
// private 생성자는 private final인 싱글턴 인스턴스를 초기화할 때 단 한번만 호출
public static final Evlis INSTANCE = new Elvis();
private Elvis() {...}
public void leaveTheBuilding() {...}
}
주의!
이 인스턴스가 싱글턴임이 보장되지만 유일하게 예외가 있는데, 리필렉션 API인AccessibleObject.setAccessible
을 사용해 private 생성자를 호출할 수 있다.
- 이를 예방하려면 생성자 차원에서 2번째 객체가 생성되려 할 때 예외를 던지게 하라!
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() {...}
// 동일 객체의 참조를 반환하기에 싱글턴 보장(단, 이 경우에도 리플렉션을 통한 예외는 동일)
public static Elvis getInstance() {return INSTANCE;}
public void leaveTheBuilding() {...}
}
Elvis::getInstance
를 Supplier<Elvis>
로 사용싱글턴 클래스를 직렬화하려면 단순히 Serializable
를 구현하는 것으로는 부족하다.
직렬화 하려면
이렇게 하지 않을 경우 역직렬화할 때 마다 새로운 인스턴스가 만들어진다.
// readResolve 메서드 예시
private Object readResolve() {
// '진짜' Elvis를 반환하고, 가짜 Elvis는 가비지 컬렉터에 맡긴다.
return INSTANCE;
}
원소가 하나인 열거 타입을 선언하는 방식
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() {...}
싱글톤을 보장하는 방식은 3가지가 있다.
- 대부분의 경우 우너소가 하나뿐인 열거 타입을 사용해 싱글톤을 보장하라!
- 만약 상속이 필요하다면 private static final 필드 방식 혹은 정적 팩터리 방식을 사용해 싱글톤을 보장하라
인스턴스를 만들어 사용하려고 설계한 클래스가 아니다. 그러므로 혹시라도 인스턴스가 만들어지지 않도록 설계해야한다.
- `java.lang.Math`와 `java.util.Array`: 기본 타입 값이나 배열 관련 메서드를 모아놓음
- `java.util.Collections`: 특정 인터페이스를 구현하는 객체를 생성해주는 정적 메서드(혹은 팩터리) 모음(자바 8부터는 인터페이스에 넣을 수 있다)
- final 클래스와 관련한 메서드를 모을 경우: final클래스를 상속해서 하위 클래스에 메서드를 넣는 건 불가능하기 때문
- 추상 클래스를 상속받은 하위 클래스를 만들어서 인스턴스화할 수 있다.
- 추상 클래스는 보통 상속에서 많이 쓰이므로 상속해서 사용하라는 의미로 오독될 수도 있다.
private 생성자를 추가하여 클래스의 인스턴스화를 막아라!
→ 생성자를 막았으므로 상속의 위험 또한 없다!