# Item 3. Ensure Singleton with Private Constructor or Enum Type

싱글턴(singleton)이란 인스턴스를 오직 하나만 생성할 수 있는 클래스를 의미한다. 싱글턴 패턴은 무상태(stateless) 객체나 시스템 컴포넌트에서 유용하다. 그러나 싱글턴을 올바르게 구현하지 않으면 다양한 문제가 발생할 수 있다. 여기서는 싱글턴 패턴을 안전하게 구현하는 방법을 소개한다.

**싱글턴 패턴의 문제점**

싱글턴 패턴은 일반적으로 두 가지 방식으로 구현된다. 첫 번째는 public static final 필드를 사용하는 방식이고, 두 번째는 정적 팩터리 메서드를 사용하는 방식이다.

```java
public class Elvis {
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() { ... }

    public void leaveTheBuilding() { ... }
}
```

이 방법의 장점은 단순하고 직관적이라는 것이다. 그러나 리플렉션을 사용하면 private 생성자를 호출할 수 있어 싱글턴 패턴이 깨질 수 있다. 이를 방지하려면 생성자를 수정하여 두 번째 인스턴스 생성 시 예외를 던지도록 해야 한다.

```java
public class Elvis {
    public static final Elvis INSTANCE = new Elvis();
    private static boolean instanceCreated = false;

    private Elvis() {
        if (instanceCreated) {
            throw new IllegalStateException("Already created.");
        }
        instanceCreated = true;
    }

    public void leaveTheBuilding() { ... }
}
```

또한, 싱글턴 클래스가 직렬화 가능한 클래스라면 readResolve 메서드를 제공해야 한다. 그렇지 않으면 역직렬화 과정에서 새로운 인스턴스가 생성될 수 있다.

```java
private Object readResolve() {
    return INSTANCE;
}
```

**정적 팩터리 메서드를 사용하는 싱글턴 패턴**

정적 팩터리 메서드를 사용하면 더 유연하게 싱글턴을 구현할 수 있다. API를 변경하지 않고도 싱글턴 방식에서 변경할 수 있으며, 메서드 참조를 공급자(supplier)로 사용할 수 있다.

```java
public class Elvis {
    private static final Elvis INSTANCE = new Elvis();
    private Elvis() { ... }

    public static Elvis getInstance() {
        return INSTANCE;
    }

    public void leaveTheBuilding() { ... }
}
```

정적 팩터리 메서드는 공급자 인터페이스를 사용할 수 있으며, 유일한 인스턴스를 반환하기 때문에 단위 테스트 작성 시 가짜(mock) 객체로 대체하기 쉽다.

**열거 타입으로 싱글턴 구현하기**

가장 간단하고 안전한 싱글턴 구현 방법은 열거 타입을 사용하는 것이다. 이 방법은 직렬화와 리플렉션 공격에서도 안전하다.

```java
public enum Elvis {
    INSTANCE;

    public void leaveTheBuilding() { ... }
}
```

이 방법은 다른 방법들보다 간결하고 추가 작업 없이 직렬화가 가능하다. 열거 타입은 인스턴스가 하나뿐임을 보장하며, 복잡한 직렬화 상황에서도 제2의 인스턴스가 생성되지 않는다.

**결론**

싱글턴 패턴을 구현할 때는 주의해야 할 점이 많다. private 생성자를 사용하거나 열거 타입을 사용하면 대부분의 문제를 해결할 수 있다. 특히 열거 타입을 사용하면 싱글턴을 안전하고 쉽게 구현할 수 있다. 이러한 방법을 통해 싱글턴 패턴의 장점을 최대한 활용할 수 있을 것이다.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wonjoon.gitbook.io/joons-til/books/effective-java/item-3.-ensure-singleton-with-private-constructor-or-enum-type.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
