import java.io.Serializable;
public final class Elvis implements Serializable {
public static final Elvis INSTANCE = new Elvis();
private Elvis() { }
public static Elvis getINSTANCE() {
return INSTANCE;
}
// readResolve 메서드를 정의한다.
private Object readResolve() {
// 싱글턴을 보장하기 위함!
return INSTANCE;
}
}
디폴트 직렬화 방법을 사용하면 인스턴스를 복원할 때마다 새로운 인스턴스가 생성
→ readResolve() 메서드 사용
역직렬화를 통해 객체가 복원될 때 호출
readResolve() 메서드
Object 타입을 반환하며,
반환값은 실제로 복원될 객체를 나타냄
이 메서드가 반환한 객체는 복원된 객체로 대체
메서드 정의 후
역직렬화 과정에서도 항상 같은 인스턴스를 반환
→싱글턴 객체가 보장
import java.io.Serializable;
public final class Elvis implements Serializable {
public static final Elvis INSTANCE = new Elvis();
private Elvis() { }
public static Elvis getINSTANCE() {
return INSTANCE;
}
private Object readResolve() {
return INSTANCE;
}
}
INSTANCE 변수는 static으로 선언되어 있어서 클래스 로딩 시점에 생성됩니다. 따라서 readResolve() 메서드에서 반환하는 객체는 항상 INSTANCE 변수를 참조하게 됨
컴파일 타임에 어떤 인스턴스들이 있는지 알 수 없는 상황에는 열거 타입으로 표현하는 것이 불가능하므로 readResolve 방식을 써야 한다.
2. Transient 키워드
열거 타입(Enumeration Type)?
컴파일 타임에 선언된 상수 값들의 집합을 나타내는 특별한 데이터 타입
따라서 런타임에 객체가 동적으로 생성되는 상황에서는 어떤 인스턴스들이 있는지 컴파일 타임에 알 수 없음!
Elvis 인스턴스의 직렬화 형태는 아무런 실 데이터를 가질 필요가 없으니 모든 인스턴스 필드는 transient로 선언해야 한다.
Transient?
Java에서 객체의 직렬화(Serialization) 대상에서 제외시키는 데 사용
즉, transient 키워드가 붙은 필드는 직렬화될 때 객체의 상태를 저장하는 데이터 스트림에서 제외됨
주로 보안 또는 성능 최적화 목적으로 사용
예를 들어, 암호화된 비밀번호나 세션 토큰과 같은 중요한 데이터를 객체에 저장할 때, 이 필드가 직렬화되어 파일이나 네트워크를 통해 전송될 경우 보안이 위협되는 경우 사용함
public class Person implements Serializable {
private String name;
private transient String password;
public Person(String name, String password) {
this.name = name;
this.password = password;
}
// password 필드는 직렬화에서 제외됨
public String toString() {
return "Person [name=" + name + ", password=" + password + "]";
}
}
Person 클래스의 password 필드에 transient 키워드가 적용되어 있으므로, 이 필드는 객체의 직렬화 대상에서 제외된다.
→ 즉, toString() 메서드에서 password 필드를 출력하더라도, 직렬화된 객체에는 password 필드가 포함되지 않게 되는 것
핵심 정리
불변식을 지키기 위해 인스턴스를 통제해야 한다면 가능한 한 열거 타입을 사용
여의치 않은 상황에서 직렬화와 인스턴스 통제가 모두 필요하다면 readResolve 메서드를 작성해 넣어야 하고, 그 클래스에서 모든 참조 타입 인스턴스 필드를 transient로 선언해야 한다.
1. Serializable 인터페이스를 구현하는 예시
디폴트 직렬화 방법을 사용하면 인스턴스를 복원할 때마다 새로운 인스턴스가 생성
→ readResolve() 메서드 사용
역직렬화를 통해 객체가 복원될 때 호출
readResolve() 메서드
메서드 정의 후
역직렬화 과정에서도 항상 같은 인스턴스를 반환
→싱글턴 객체가 보장
INSTANCE
변수는static
으로 선언되어 있어서 클래스 로딩 시점에 생성됩니다. 따라서readResolve()
메서드에서 반환하는 객체는 항상INSTANCE
변수를 참조하게 됨컴파일 타임에 어떤 인스턴스들이 있는지 알 수 없는 상황에는 열거 타입으로 표현하는 것이 불가능하므로
readResolve
방식을 써야 한다.2. Transient 키워드
열거 타입(Enumeration Type)?
Elvis 인스턴스의 직렬화 형태는 아무런 실 데이터를 가질 필요가 없으니 모든 인스턴스 필드는 transient로 선언해야 한다.
Transient?
transient
키워드가 붙은 필드는 직렬화될 때 객체의 상태를 저장하는 데이터 스트림에서 제외됨Person
클래스의password
필드에transient
키워드가 적용되어 있으므로, 이 필드는 객체의 직렬화 대상에서 제외된다.→ 즉,
toString()
메서드에서password
필드를 출력하더라도, 직렬화된 객체에는password
필드가 포함되지 않게 되는 것핵심 정리