[Effective Java] 아이템 89. 인스턴스 수를 통제해야 한다면 readResolve 보다는 열거 타입을 사용하라

2023. 1. 4. 23:22· BE/Java
목차
  1. 아이템 89. 인스턴스 수를 통제해야 한다면 readResolve 보다는 열거 타입을 사용하라
반응형

EFFECTIVE JAVA(이펙티브 자바)

 

이 포스팅에서 작성하는 내용은 EFFECTIVE JAVA(이펙티브자바) 에서 발췌하였습니다.


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

 

ex 1) 생성자 호출을 막은 싱글턴 패턴 예시

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

    public void leaveTheBuilding() { ... }
}
  • 이 클래스는 바깥에서 생성자를 호출하지 못하게 막는 방식(private)으로 인스턴스가 오직 하나만 만들어짐을 보장했다.
  • 아이템 3에서 이야기했듯 이 클래스는 선언에 implements Serializable을 추가하는 순간 싱글턴이 아니게 된다.
  • 기본 직렬화를 쓰지 않더라도, 명시적인 readObject를 제공하더라도 싱글턴이 아니다.
  • 어떤 readObject를 사용하든간에 이 클래스가 초기화될 때 만들어진 인스턴스와는 별개의 인스턴스를 반환하게 된다.

 

 

readResolve

  • readObject가 만들어낸 인스턴스를 다른 것으로 대체할 수 있다.
  • 역직렬화한 객체의 클래스가 readResolve 메서드를 적절히 정의해뒀다면, 역직렬화 후 새로 생성된 객체를 인수로 이 메서드가 호출되고, 이 메서드가 반환한 객체 참조가 새로 생성된 객체를 대신해 반환된다.
  • 대부분의 경우 이 때 새로 생성된 객체의 참조는 유지하지 않으므로 GC의 대상이 된다.

 

 

ex 2) Serializable을 구현한 Elvis의 readResolve 메서드를 이용한 예시

private Object readResolve() {
    // 진짜 Elvis를 반환하고, 가짜는 알아서 GC에 맡겨짐
    return INSTANCE;
}
  • 이 메서드는 역직렬화한 객체는 무시하고 클래스 초기화 때 만들어진 Elvis 인스턴스를 반환한다.
  • Elvis 인스턴스 직렬화 형태는 실 데이터를 가질 이유가 없으니 모든 인스턴스 필드를 transient로 선언해야 한다.
  • **사실, readResolve를 인스턴스 통제 목적으로 사용한다면 객체 참조 타입 인스턴스 필드는 모두 transient로 선언해야 한다. (**그렇지 않으면 아이템 88에서 언급한 공격을 받을 수 있다.)
  • 공격 방법
    • 싱글턴이 transient가 아닌 참조 필드를 가지고 있다면, 그 필드의 내용은 readResolve 메서드가 실행되기 전에 역직렬화 된다.
    • 그렇다면 잘 조작된 스트림을 써서 해당 참조 필드의 내용이 역직렬화되는 시점에 그 역직렬화된 인스턴스의 참조를 훔쳐올 수 있다.

 

 

해결 방법 - 열거 타입

  • 직렬화 가능한 인스턴스 통제 클래스를 열거 타입을 이용해 구현하면 선언한 상수 외의 다른 객체는 존재하지 않음을 자바가 보장해준다.
  • 물론 공격자가 AccessibleObject.setAccessible 같은 특권 메서드를 악용한다면 이야기가 달라진다.
    (임의의 네이티브 코드를 수행할 수 있는 특권을 가로챈 공격자에게는 모든 방어가 무력화된다.)

 

 

ex 3) 위의 Elvis 를 열거 타입으로 구현한 예시

public enum Elvis {
    INSTANCE;

    private String[] favoriteSongs = { "Hound Dog", "Heartbreak Hotel" };

    public void printFavorites() {
        System.out.println(Arrays.toString(favoriteSongs));
    }
}
  • 인스턴스 통제를 위해 readResolve를 사용하는 방식이 완전히 쓸모없는 것은 아니다.
  • 직렬화 가능 인스턴스 통제 클래스를 작성해야 하는데, 컴파일 타임에 어떤 인스턴스들이 있는 지 알 수 없는 상황이라면 열거 타입으로 표현하는 것이 불가능하기 때문이다.
  • readResolve 메서드의 접근성은 매우 중요하다.
    • final 클래스에서라면 readResolve 메서드는 private 이어야 한다.
    • final이 아닌 클래스에서는 다음의 몇 가지를 주의해서 고려해야 한다.
      • private 으로 선언하면 하위 클래스에서 사용할 수 없다.
      • package-private으로 선언하면 같은 패키지에 속한 하위 클래스에서만 사용할 수 있다.
      • protected나 public 으로 선언하면 이를 재정의하지 않은 모든 하위 클래스에서 사용할 수 있다.
      • protected나 public이면서 하위 클래스에서 재정의하지 않았다면, 하위 클래스의 인스턴스를 역직렬화하면서 상위 클래스의 인스턴스를 생성하여 ClassCastException을 일으킬 수 있다.

 

 

정리

  • 불변식을 지키기 위해 인스턴스를 통제해야 한다면 열거 타입을 사용하자.
  • 여의치 않은 상황에서 직렬화와 인스턴스 통제가 모두 필요하다면 readResolve 메서드를 작성해 넣어야 하고, 그 클래스에서 모든 참조 타입 인스턴스 필드를 transient로 선언하자.
반응형

'BE > Java' 카테고리의 다른 글

[Junit 5] Junit5 들어가기  (0) 2023.02.07
[Effective Java] 아이템 90. 직렬화된 인스턴스 대신 직렬화 프록시 사용을 검토하라  (0) 2023.01.05
[Effective Java] 아이템 88. readObject 메서드는 방어적으로 작성하라  (0) 2023.01.03
[Effective Java] 아이템 87. 커스텀 직렬화 형태를 고려해보라  (1) 2023.01.02
[Effective Java] 아이템 86. Serializable을 구현할지는 신중히 결정하라  (0) 2022.12.31
  1. 아이템 89. 인스턴스 수를 통제해야 한다면 readResolve 보다는 열거 타입을 사용하라
'BE/Java' 카테고리의 다른 글
  • [Junit 5] Junit5 들어가기
  • [Effective Java] 아이템 90. 직렬화된 인스턴스 대신 직렬화 프록시 사용을 검토하라
  • [Effective Java] 아이템 88. readObject 메서드는 방어적으로 작성하라
  • [Effective Java] 아이템 87. 커스텀 직렬화 형태를 고려해보라
멍목
멍목
개발 관련 새롭게 알게 된 지식이나 좋은 정보들을 메모하는 공간입니다.
반응형
멍목
김멍목의 개발블로그
멍목
전체
오늘
어제
  • 분류 전체보기 (514)
    • BE (190)
      • Spring (21)
      • Java (141)
      • Kotlin (6)
      • JPA (22)
    • FE (33)
      • Javascript (16)
      • Typescript (0)
      • React (5)
      • Vue.js (9)
      • JSP & JSTL (3)
    • DB (32)
      • Oracle (22)
      • MongoDB (10)
    • Algorithm (195)
    • Linux (8)
    • Git (6)
    • etc (42)
    • ---------------------------.. (0)
    • 회계 (4)
      • 전산회계 2급 (4)
    • 잡동사니 (2)

블로그 메뉴

  • 홈
  • 관리

공지사항

인기 글

태그

  • 코테 공부
  • MongoDB with Node.js
  • 자바 테스팅 프레임워크
  • Effective Java
  • 코틀린
  • 알고리즘 공부
  • 자바 공부
  • 이펙티브자바
  • 프로젝트로 배우는 Vue.js 3
  • 자바공부
  • 더 자바 Java 8
  • 자기 공부
  • 코테공부
  • 자기개발
  • 전산회계 2급 준비
  • junit5
  • java 8
  • 자바 개발자를 위한 코틀린 입문
  • 자기공부
  • 이펙티브 자바
  • vue3 공부
  • 자기 개발
  • JPA 공부
  • JPA
  • MongoDB 기초부터 실무까지
  • Oracle
  • Java to Kotlin
  • 더 자바 애플리케이션을 테스트하는 다양한 방법
  • 알고리즘공부
  • MongoDB 공부

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.2.0
멍목
[Effective Java] 아이템 89. 인스턴스 수를 통제해야 한다면 readResolve 보다는 열거 타입을 사용하라
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.