// 아이템 3에서 만들었던 싱글톤 패턴

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

    public void leaveTheBuilding() { ... }
}
  • 이 클래스 선언에 implements Serializable을 추가하는 순간 더 이상 싱글턴이 아니게 된다.
  • 어떤 readObject를 사용하든 이 클래스가 초기화될 때 만들어진 인스턴스와는 별개인 인스턴스를 반환하게 된다.

 

 

readResolve 기능을 이용하면 readObject가 만들어낸 인스턴스를 다른 것으로 대체할 수 있다.

  • 역직렬화한 객체의 클래스가readResolve 메소드를 적절히 정의해뒀다면, 역직렬화 후 새로 생성된 객체를 인수로 이 메소드가 호출되고, 이 메소드가 반환한 객체 참조가 새로 생성된 객체를 대신해 반환된다.
  • readResolve 메소드를 추가해 싱글톤 속성을 유지할 수 있다.
// 인스턴스 통제를 위한 readResolve
private Object readResolve() {

    // 진짜 Elvis를 반환하고 가짜 Elvis는 가비지 컬렉터에 맡긴다.
    return INSTANCE;
}
  • 이 메소드는 역직렬화한 객체는 무시한다.
  • 따라서 Elvis는 transient로 선언해야 한다.
  • 사실 readResolve를 인스턴스 통제 목적으로 상요한다면 객체 참조 타입 인스턴스 필드는 모두 transient로 선언해야 한다.
// 잘못된 싱글턴 - transient가 아닌 참조필드를 가지고 있다.

public class Elvis implements serializable {
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() {}

    private String[] favoriteSongs =
        {"Hound Dog", "Heartbreak Hotel"};
    public void printFavoirtes() {
        System.out.println(Arrays.toString(favoriteSongs));
    }

    private Obejct readResolve() {
        return INSTANCE;
    }
}
// 객체 참조를 훔치는 도둑 클래스

public class ElvisStealer implments Serializable {
    static Elvis impersonator;
    private Elvis payload;

    private Object readResolve() {
        // resolve 되기 전의 Elvis 인스턴스의 참조를 저장
        impersonator = payload;

        // favoriteSongs 필드에 맞는 타입의 객체를 반환
        return new String[] {"A Fool Such as I"};
    }
    private static final long serialVersionUID = 0;
}

 

 

 

직렬화 가능한 인스턴스 통제 클래스를 열거타입을 통해 구현

  • 선언한 상수 외의 다른 객체는 존재하지 않음을 자바가 보장해줌
// 열거 타입 싱글톤 - 전통적인 싱글톤보다 우수하다.

public enum Elvis {
    INSTANCE;
    private String[] favoriteSongs =
        {"Hound Dog", "Heartbreak Hotel"};
    public void printFavoirtes() {
        System.out.println(Arrays.toString(favoritSongs));
    }

}

+ Recent posts