public static 멤버가 final 필드인 방식
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() {...}
public void leaveTeBuilding() {
}
}
외부에서 Elvis.INSTANCE
로 객체를 가져올 수 있다.
com.maxst.maxwork.remote.Elvis@25865fca
com.maxst.maxwork.remote.Elvis@4c32aa7e
예제코드1
을 실행시키면 다음과 같이 IllegalStateException
이 발생하며, 객체 2개 이상 생성을 막을 수 있다.public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() {...}
public Elvis getInstance() { return INSTANCE;}
public void leaveTeBuilding() {
}
}
외부에서 Elvis.getInstance()
로 객체를 가져올 수 있다.
public Elvis getInstance() { return new Elvis();}
Elvice::getInstance 를 Supplier로 사용
Supplier<Elvis> supplier = (Supplier<Elvis>) Elvis.getInstance();
Elvis elvis = supplier.get();
2번방식의 장점이 필요하지 않다면 첫번째 방식으로 하는 것이 좋다.
transient
키워드를 선언한다.transient
키워드를 선언하면, 키워드를 붙인 멤버필드는 직렬화에서 제외된다는 속성을 이용하였다.transient
를 모든 멤버변수에 붙이는 것은 번거롭다.참고한 사이트
https://100100e.tistory.com/342
https://madplay.github.io/post/what-is-readresolve-method-and-writereplace-method
https://madplay.github.io/post/java-serialization
https://madplay.github.io/post/what-is-readobject-method-and-writeobject-method
private constructor
가 호출되는 문제도 고민할 필요가 없는 방법이다.public enum ElvisEnum {
INSTANCE;
String name;
int age;
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
ElvisEnum singleton = ElvisEnum.INSTANCE;
System.out.println(singleton.getName());
singleton.setAge(2);
enum 은 프로그래밍 언어에 국한 되지않고 공통적으로 명명된 상수들의 집합, 즉 열거를 의미한다. enum 이라는 네이밍에
열거
라는 구현의 의도가 명확하게 드러난다. 과연 enum을 싱글턴으로 썼을 때 목적을 쉽게 파악할 수 있을까라는 의문도 생겼다.
분명 enum을 싱글톤으로 사용했을 때의 장점은 크다.
Thread-Safe 하고, 리플렉션 공격에도 안전하고, Serialization 도 보장하는 Singleton 이 가지는 모든 단점을 커버한다.
Joshua Bloch 는 책에서도 enum 방식이 널리 채택되어지지 않았다는 것을 인지하지만 싱글턴을 구현하는 최적의 방법이 enum 이라고 한다.
enum 으로 싱글턴을 구현하는 방법이 널리 쓰이지 않는데는, java 진영은 대부분 Spring 을 사용하고 있고, Spring은 싱글턴패턴이 안티패턴이므로 컨테이너를 통해 직접 빈들을 싱글톤으로 관리하도록 개발할 수 있도록 제공하기 때문이라고 생각한다. 굳이 싱글톤을 사용할 필요가 없다.
enum 으로 싱글턴을 구현하는 것을 극도로 싫어하는 사람이 있는데, 이해는 간다. 하지만 정말 자원을 한정적으로 전역자원으로 써야할 필요가 있다면, 그야말로 Minimum Effort, Maximum Effect 이 아닐까. 극도로 싫어하는 사람도 있지만, 소수 enum 으로 구현하기를 선호하는 사람도 있다.
예제코드
public interface SingletonInterface {
int getNum();
}
-----------------------------------------------
public enum SingletonObject implements SingletonInterface {
INSTANCE;
private int num;
protected void setNum(int num) {
this.num = num;
}
@Override
public int getNum() {
return num;
}
}
----------------------------------------------------
@Test
public void test() {
SingletonInterface singleton = Mockito.mock(SingletonInterface.class);
when(singleton.getNum()).thenReturn(1); //does work
}
한정된 자원 안에서 인스턴스를 남용하지 않고, 하나의 자원으로 모두가 공유해서 사용해야하는 경우 유용한 방법이 될 수 있다.