인스턴스를 오직 한개만 제공하는 클래스
public class Settings {
private static Settings instance;
private Settings() {}
public static Settings getInstance() {
if (instance == null) {
instance = new Settings();
}
return instance;
}
}
@Test
void singleTon() {
Settings settings = Settings.getInstance();
Settings settings1 = Settings.getInstance();
assertThat(settings == settings1).isTrue();
}
public class Settings {
private static Settings instance;
private Settings() {}
public static synchronized Settings getInstance() {
if (instance == null) {
instance = new Settings();
}
return instance;
}
}
public class Settings {
private static final Settings INSTANCE = new Settings();
private Settings() {}
public static Settings getINSTANCE() {
return INSTANCE;
}
}
public class Settings {
private static volatile Settings instance;
private Settings() {}
public static Settings getINSTANCE() {
if (instance == null) {
synchronized (Settings.class) {
if (instance == null) {
instance = new Settings();
}
}
}
return instance;
}
}
public class Settings {
private Settings() {}
private static class SettingsHolder {
private static final Settings INSTANCE = new Settings();
}
public static Settings getInstance() {
return SettingsHolder.INSTANCE;
}
}
리플렉션, 직렬화 & 역직렬화 등을 사용하면 싱글톤 패턴이 깨질 수 있다.
enum을 사용하면 싱글톤 패턴을 안전하고 단순하게 구현할 수 있다.
public enum SettingsEnum {
INSTANCE;
}
@Test
void enumSingleTon() {
SettingsEnum settings = SettingsEnum.INSTANCE;
SettingsEnum settings1 = SettingsEnum.INSTANCE;
assertThat(settings == settings1).isTrue();
}
enum은 리플렉션에서 newInstance()를 할 수 없다. (리플렉션을 사용하더라도 싱글톤이 보장된다.)
단점은 enum은 애플리케이션 로딩 시점에 인스턴스가 생성된다. (로딩 시점에 생성해놓고 사용하지 않는다면 리소스에 낭비가 생길 수 있다.)
serializable을 구현하고 있다.
상속을 사용할 수 없다. (인터페이스 구현은 가능)
상속이 필요하고, 지연 로딩까지 하고 싶은 경우 static inner 클래스를 사용하는 것이 좋다.
스프링 컨테이너에서는 모든 인스턴스가 싱글톤 객체로 관리된다.
스프링 컨테이너는 엄밀히 따지자면 싱글톤 패턴이 아니라 싱글톤 스코프다.