Description
Discussed in https://github.com/orgs/Study-2-Effective-Java/discussions/204
Originally posted by bunsung92 April 10, 2023
0. TL;DR 📚
- 불변식을 지키기 위해 인스턴스를 통제한다면, 열거 타입을 이용하자.
- 직렬화와 인스턴스 통제가 모두 필요하면
readResolve()
작성하고, 클래스의 모든 참조 타입 인스턴스 필드를transient
키워드를 이용하자.
1. 인스턴스 수의 통제
인스턴스 수를 통제하는 기법은 바로 싱글턴
이다.
그리고 직렬화
와 싱글턴
은 상성이 맞지 않다.
바로 implement Serializable
을 추가하는 순간 싱글턴이 아니게 되기 때문이다.
기본 직렬화를 쓰지 않더라도 (아이템 87)
명시적인 readObject()
(아이템88) 를 제공하더라도 소용없다.
어떤 readObject()
를 사용하든 이 클래스가 초기화될 때 만들어진 인스턴스와는 별개의 인스턴스를 반환하기 때문이다.
2. readResolve()
readObject()
가 별개의 인스턴스를 반환하기만을 마냥 기다릴 수 없다.
readResolve()
를 가용하여 필연적으로 원본의 Instance를 반환하게 지시할 수 있다.
public class Foo {
private static final Foo INSTANCE = new Foo();
private Foo() {
}
public static Foo getINSTANCE() {
return INSTANCE;
}
// readResolve 메서드를 정의한다.
private Object readResolve() {
// 싱글턴을 보장하기 위함!
return INSTANCE;
}
}
이때 만약에 싱글턴 클래스의 필드가 존재한다면, 해당 필드는 반드시 모두다 transient
키워드를 추가해야한다.
readResolve()
가 수행되기전에 역 직렬화된 객체의 참조를 공격할 여지가 남는다.
2.1 deep To readResolve()
- readObject()는 ObjectInputStream클래스 의 기존 메서드임.
- 역직렬화 readObject()메서드는 역직렬화 중인 개체에 readResolve()메서드가 구현되어 있는지 여부를 내부적으로 확인함.
- 메소드가 존재 하면 readResolve()호출됨.
즉 구현 여부를 판단하여
readResolve()
를 호출하기 전에 어떤 동작을 해버리면 막기 어려워진단 말임!
3. 싱글턴과 열거 타입
-
직렬화 가능한 인스턴스 통제 클래스를 열거 타입을 이용해 구현하면, 선언한 상수 외의 다른 객체가 존재하지 않는다는것을
자바
가 보증한다.- 하지만
native 코드
까지 조작한다면 이는 완전하지 않을 수 있다.
- 하지만
-
readResolve()는 완전히 쓸모 없어진것이 아니다.
- 직렬화 가능 인스턴스 통제 클래스를 작성해야 하는데, 컴파일에는 어떤 인스턴스들이 있는지 알 수 없는 상황이라면, 열거타입으로 표현하는 것이 불가능하기 때문이다.
3.1 readResolve()와 접근제어자
- final Class 에서는 readResolve()는 private으로 선언해야한다.
- final Class가 아닌 경우에는 package-private, protected, public으로 해당 메서드를 정의 할 수 있는데 이때 주의 할 점이있다.
하위 클래스에서 readResolve()를 재정의 하지 않고 형 변환을 통해 해당 직렬화를 이용 했다면,
ClassCastException
이 발생한다.
정리하자면 반드시 하위 클래스에서는 readResolve()를 정의해야 한다는 것이다!
4. 핵심 정리 📚
- 내가 커스텀 직렬화를 하고 있고, 싱글턴 인스턴스를 보장 해야한다면, 두가지 선택권이 있다는걸 인지하자.
1. readResolve()
2. 열거타입
- 반드시 1이 좋고, 2가 좋다. 보다는 각 상황에 알맞게 사용하도록 하자.
- 고려할 사항은 열거 타입이
readResolve()
보다 적다.
5. 회고 🧹
2023-04-23 (일)
- 해당 아이템의 회고보다 마지막 회고는 시점에 대한 느낌을 남기고 싶다.
- 마지막 아이템을 맡아 마지막 정리를 하게 되었고, 자바의 여러 재미를 알아 볼 수 있어서 좋았던 것 같다.
- 언젠간 다시 이 페이지로 돌아와, 우리 7인의 노고를 다시 보게 된다면, 이 시점에 우린 이렇게 살고 있었구나 느낄 수 있는 작은 발자취가 되었다고 생각한다.
참조 자료
https://madplay.github.io/post/what-is-readresolve-method-and-writereplace-method
https://stackoverflow.com/questions/1168348/java-serialization-readobject-vs-readresolve
https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%A7%81%EB%A0%AC%ED%99%94Serializable-%EC%99%84%EB%B2%BD-%EB%A7%88%EC%8A%A4%ED%84%B0%ED%95%98%EA%B8%B0