MOCK
구현으로 대체할 수 없기 떄문이다.public class Elvis {
//Elvis.INSTANCE 를 초기화시에 `private` 생성자아 딱 한 번만 호출되어 인스턴스가 하나만 생서됨을 보증할 수 있다.
public static final Elvis INSTANCE = new Elvis();
private Elvis() {
}
public void leaveTheBuilding() {
}
}
public class Main {
public static void main(String[] args) {
Elvis elvis = Elvis.INSTANCE;
elvis.leaveTheBuilding();
Elvis elvis2 = new Elvis(); // 생성자가 private 이라 호출 불가능
}
}
둘다 같은 객체를 가리기고있음을 알 수 있다.
JVM 에서 해당 객체는 어떤식으로 메모리를 할당받는걸까?
Elvis
클래스의 INSTANCE
필드를 초기화 한다.Elvis.INSTANCE
는 정적 필드이기에, JVM 의 메서드 영역에 저장된다.new Elvis()
는 힙 영역에 Elvis
객체를 생성한다.Elvis.INSTANCE
를 호출하는 경우 메모리의 같은 객체를 참조한다.같은 객체를 가리키는 이유 와 생성자가 왜 계속 호출되지 않는지?
public static final Elvis INSTANCE = new Elvis()
는 클래스가 로드될 때 한 번만 실행됨.INSTANCE
를 통해 같은 객체를 재사용하게 됨.public class ElvisStaticFactory {
private static final ElvisStaticFactory INSTANCE = new ElvisStaticFactory();
private ElvisStaticFactory() {
}
public static ElvisStaticFactory getInstance() {
return INSTANCE;
}
public void leaveTheBuilding() {
}
}
나중에 싱글턴이 아니도록 변경할 수 있다
public class Elvis {
private static Elvis instance;
private Elvis() {
// private 생성자
}
public static Elvis getInstance() {
if (instance == null) {
instance = new Elvis();
}
return instance;
}
public void leaveTheBuilding() {
System.out.println("Elvis has left the building!");
}
}
정적 팩토리를 제네릭 싱글턴 팩토리로 만들 수 있다.
public class SingletonFactory {
private static final Map<Class<?>, Object> instances = new HashMap<>();
private SingletonFactory() {
// private 생성자
}
@SuppressWarnings("unchecked")
public static <T> T getInstance(Class<T> clazz) {
return (T) instances.computeIfAbsent(clazz, SingletonFactory::createInstance);
}
private static <T> T createInstance(Class<T> clazz) {
try {
return clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}