Skip to content

아이템 89. 인스턴스 수를 통제해야 한다면 readResolve보다는 열거 타입을 사용하라 - Fin #210

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Irisation23 opened this issue Apr 23, 2023 Discussed in #204 · 0 comments
Assignees
Labels
12장 직렬화 이펙티브 자바 12장 (직렬화)

Comments

@Irisation23
Copy link
Member

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()를 사용하든 이 클래스가 초기화될 때 만들어진 인스턴스와는 별개의 인스턴스를 반환하기 때문이다.

image


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() 를 호출하기 전에 어떤 동작을 해버리면 막기 어려워진단 말임!

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

@Irisation23 Irisation23 added the 12장 직렬화 이펙티브 자바 12장 (직렬화) label Apr 23, 2023
@Irisation23 Irisation23 self-assigned this Apr 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
12장 직렬화 이펙티브 자바 12장 (직렬화)
Projects
None yet
Development

No branches or pull requests

1 participant