조슈아 블로크 님의 "Effective Java" 책을 정리한 포스팅 입니다.
4. readObject 메서드는 방어적으로 작성하라
readObject()는 또다른 public 생성자
- 직렬화된 객체의 상태를 복원하는 메서드
- 복원 중에 새로운 객체를 생성하는 역할을 함
매개변수로 바이트 스트림을 받는 생성자
- 바이트 스트림을 받아서 객체 상태를 읽고 복원하는 방식
- 바이트 스트림이 유효한지 먼저 검사해야 함
인수가 유효한지 검사
- defaultReadObject를 호출하여 기본 상태 복원 및 유효성 검사
매개변수를 방어적으로 복사해야 함
- 클라이언트가 소유해서는 안되는 객체 참조를 갖는 필드를 모두 반드시 방어적으로 복사해야 함
- final은 방어적 복사 불가
clone()은 어떻게 복사될지 모르므로 사용하지 않도록
- 객체가 어떻게 복사될 지 예측하기 어려움
- 명시적으로 깊은 복사를 구현하거나 새로운 객체를 생성하는 방식으로 안전하게 복사해야 함
직렬화 프록시 사용하기
5. 인스턴스 수를 통제해야 한다면 readResolve보다는 열거 타입을 사용하라
readResolve()
public class Singleton implements Serializable {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
// private constructor to prevent instantiation
}
// Singleton instance retrieval method
public static Singleton getInstance() {
return INSTANCE;
}
// readResolve() method to ensure singleton behavior during deserialization
private Object readResolve() throws ObjectStreamException {
return INSTANCE;
}
}
- readObject가 만들어낸 인스턴스를 다른 것으로 대체할 수 있음
- 직렬화된 객체가 역직렬화될 때 ObjectInputStream에 의해 호출됨
접근성이 매우 중요
- private: 외부에서 호출되지 않도록 해야 함
6. 직렬화된 인스턴스 대신 직렬화 프록시 사용을 검토하라
직렬화 프록시
- 직렬화된 객체 대신 직렬화 가능한 중첩 클래스를 사용하여 객체를 더 안전하게 직렬화하는 기법
- 위험성을 줄이고, 불필요한 접근이나 공개를 차단
중첩 클래스
public class OuterClass implements Serializable {
private static final long serialVersionUID = 1L;
private String outerState;
public OuterClass(String outerState) {
this.outerState = outerState;
}
public String getOuterState() {
return outerState;
}
// Serialization Proxy
private static class SerializationProxy implements Serializable {
private static final long serialVersionUID = 1L;
private String outerState;
// 프록시 생성자: OuterClass의 상태를 받아서 저장
public SerializationProxy(OuterClass outerClass) {
this.outerState = outerClass.outerState;
}
// 역직렬화 후 실제 객체를 생성
private Object readResolve() {
return new OuterClass(outerState);
}
}
// 직렬화된 객체 대신 SerializationProxy 사용
private Object writeReplace() {
return new SerializationProxy(this);
}
}
- 중첩 클래스를 통해 바깥 클래스의 직렬화된 상태를 안전하게 관리
- 바깥 클래스와 중첩 클래스 모두 Serializable 구현
- 중첩 클래스를 private static으로 선언
생성자
- 오직 하나
- 바깥 클래스를 매개변수로 받기
장점
- 객체 상태 캡슐화
- 호환성
- 보안성
- 내부 필드 보호
- 가짜 바이트 스트림 공격과 내부 필드 탈취 공격을 차단해줌
- 불변성 유지
- 역직렬화한 인스턴스와 원래의 직렬화된 인스턴스의 클래스가 달라도 정상 동작 (EnumSet)
제한 사항
확장 가능한 클래스
- 직렬화된 상태가 상속 계층을 통해 전달되지 못함
순환 참조
'Java' 카테고리의 다른 글
[Effective Java] 12-1. 직렬화 (0) | 2025.01.03 |
---|---|
[Effective Java] 11-2. 동기화 (2) | 2025.01.03 |
[Effective Java] 11-1. 동기화 (0) | 2025.01.01 |
[Effective Java] 10-2. 예외 (0) | 2025.01.01 |
[Effective Java] 10-1. 예외 (0) | 2025.01.01 |