열거 타입
한정된 값만 갖는 데이터 타입
public enum Week{ MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY } Week today = Week.SUNDAY;
열거된 상수는 각 하나의 Week 객체다.
싱글턴
인스턴스를 오직 하나만 생성할 수 있는 클래스
이제 싱글턴을 만드는 방식을 알아본다.
pulbic class Elvis{ public static final Elvis INSTANCE = new Elvis(); //private 생성자 private Elvis(){...} ... }
- 생성자를 private으로 감춘다.
- public static final 필드를 이용해, 객체를 딱 한번만 생성한다.
장점
- 코드가 간결하고 해당 클래스가 싱글턴임이 api에 명확히 드러난다.
취약점
- 다만 클라이언트가 권한이 충분할 경우, 리플렉션 api인 AccessibleObject.setAccessible 을 사용하면 private 생성자에 접근이 가능하다.
public class Elvis{ private static final Elvis INSTANCE = new Elvis(); private Elvis(){...} public static Elvis getInstance(){ return Instance; } }
- 항상 같은 객체의 참조를 반환하므로 싱글턴 보장
- 하지만 리플렉션 이슈 역시 존재함
장점
- 팩터리 메서드를 사용하면 싱글턴이 아니게 언제든지 변경이 가능하다.
- 원한다면 정적 팩토리를 "제네릭 싱글턴 팩토리"로 만들 수 있다.
- 정적 팩토리의 메서드 참조를 공급자 (Supplier)로 사용할 수 있다.
public class Elvis{ private static final Elvis INSTANCE = null; private Elvis(){...} public static Elvis getInstance(){ if(INSTANCE == null) INSTANE = new Elvis(); return Instance; } }
이 방법은 언제든지 static 팩터리 메서드를 수정하여 싱글턴이 아니게 만들 수 있다.
위 두 가지 방식으로 만든 싱글턴 객체를 직렬화 하려면 Serializable을 implement하는 것으론 모자라다.
모든 인스턴스 필드를 transient(직렬화 할 때 제외)로 선언하고 readResolve 메서드를 제공해야 한다.
이렇게 하지 않으면 역직렬화를 할 때 마다 새로운 인스턴스가 생겨나기 때문이다.
private Object readResolve() { // '진짜' Elvis를 반환하고 오버라이드 한 역직렬화 과정에서 생겨난 Elvis는 Garbage Collector에 맡긴다 return INSTANCE; }
public enum Elvis { INSTANCE; public void leaveTheBuliding() { ... } }
이해 +
Elvis elvis = Elvis.INSTANCE;
열거 변수를 만들어 새로운 객체를 생성하는 것처럼 보여도, heap 영역에 있는 열거 객체의 주소값을 바라보게 하는 원리다.
열거 타입 클래스를 만들고 상수를 지정하지만, 그 상수의 내용을 담고 있는 객체의 실체는 저 멀리 heap 영역에 있다.
열거 상수에는 열거 객체를 가르키는 주소가 담겨져 있는 원리이고, 열거 변수에는 열거 상수에 담겨져 있는 열거 객체의 주소값이 들어간다.설명
1번 방식과 비슷하지만 더 단순하고 더 간결하고 추가 노력없이 직렬화할 수 있는 방법이다.
어색해 보일 수 있으나 대부분 상황에서 원소가 하나 뿐인 enum이 싱글턴을 만드는 가장 좋은 방법이다.단, 만들려는 싱글턴이 enum외의 클래스를 상속해야 한다면 이 방법은 사용할 수 없다.
()