2009. 11. 17. 21:57

[Effective Java] Java 라는 프로그래밍 언어를 정확하고 유창하게 쓸 수 있도록 도와주는 지침서

이펙티브 자바이펙티브 자바 - 10점
조슈아 블로치 지음, 심재철 옮김/대웅

  Effective Java 는 Java 라는 프로그래밍 언어를 잘 사용할 수 있는 방법을 설명한 책으로 Java 를 배우는 분께 추천하고 싶은 책은 아닙니다. 이미 Java 세계에 입문한 초급 또는 중급 프로그래머에게 추천하고 싶은 책입니다. 이 페이지는 Effective Java 에서 몇 개의 문장을 발췌하여 간단히 요약한 글입니다. 저작권에 문제가 될 경우 알려주시면 자진 삭제하겠습니다.

 그리고 번역과 편집이 그리 훌륭한 편은 아닙니다. 영어가 가능하신 분들은 원서를 보는 것도 좋습니다.

서문
 
...자바 프로그래밍 언어의 문법을 배울 수 있는 책은 정말 많다...마찬가지로 자바 라이브러리나 API를 다루는 책들도 정말 많다.
  이 책은 당신이 원하는 것, 바로 자연스럽고 유창하가 자바 프로그래밍 언어를 말하는 법을 알려준다...잘 작동하는 당신의 코드를 다른 사람이 이해하기 쉽고, 고치기도 쉽고, 별 문제 없이 기능도 향상시킬 수 있게 만들 수 있는방법을 이 책에서 찾기 바란다. 아마도 예전보다 재미 있고 우아하고 세련된 방식으로 훨씬 더 좋은 프로그램을 만들 수 있을 것이다...

CHAPTER 1 개요 

CHAPTER 2 객체의 생성과 소멸

[항목 1] 생성자 대신 static factory method 사용을 고려하자

"A class can provide a public static factory method, which is simply a static method that returns an instance of the class." (static factory method 는 class 의 instance 를 리턴하는 static method 입니다.)

"One advantage of static factory methods is that, unlike constructors, they have names." (생성자와 비교하면, static factory method 는 이름을 정할 수 있는 점이 장점입니다.)

"A second advantage of static factory methods is that, unlike constructors, they are not
required to create a new object each time they're invoked." (static factory method 의 두 번째 장점은 생성자와는 달리 늘 새로운 object 를 생성할 필요가 없다는 점입니다.)

생성자는 자신이 정의된 class 의 instance 만 return 할 수 있지만, static factory method 는 자신이 선언된 것과 같은 타입의 instance 는 모두 return 할 수 있다.

static factory method 의 가장 큰 단점은 이 method 를 정의한 class 가 public 이나 protected 생성자를 제공하지으면 다른 class 가 이 class 를 상속받을 수 없다는 것이다.

static factory method 의 두 번째 단점은 다른 static method 와의 차이를 명시할 수 없다는 것이다.

[항목 2] 생성자의 parameter 가 많을 때는 builder 를 고려하자

Static factories and constructors share a limitation: they do not scale well to large numbers of optional parameters.(static factory 와 생성자는 optional parameter 의 수가 많아질 때 확장하는 것이 용이하지 않다는 한계가 있습니다.)

Traditionally, programmers have used the telescoping constructor pattern, in which you provide a constructor with only the required parameters, another with a single optional parameter, a third with two optional parameters, and so on, culminating in a constructor with all the optional parameters.(전통적으로 프로그래머는 telescoping constructor pattern - 필수 parameter 로 구성된 생성자, optional parameter 가 한 개 추가된 생성자, optional parameter 가 두 개 추가된 생성자, ... 모든 optional parameter 가 추가된 생성자를 제공 - 을 사용합니다.)

"the telescoping constructor pattern works, but it is hard to write client code when there are many parameters, and harder still to read it."(telescoping constructor pattern 은 동작에 문제가 없지만, parameter 가 많은 경우 클라이언트 코드를 작성하기 어렵고 클라이언트 코드를 읽는 것은 더욱 어렵습니다.)

A second alternative when you are faced with many constructor parameters is the JavaBeans pattern, in which you call a parameterless constructor to create the object and then call setter methods to set each required parameter and each optional parameter of interes(생성자 parameter 가 많을 경우 두 번째 대안은 JavaBeans pattern - parameter 가 없는 생성자를 호출하여 object 를 생성한 후 setter method 를 호출하여 필수 parameter 와 optional parameter 를 설정 - 을 사용하는 것입니다.)

"a JavaBean may be in an inconsistent state partway through its construction."(JavaBean 은 생성 중에 일관성을 잃을 수 있습니다)

"the JavaBeans pattern precludes the possibility of making a class immutable"(JavaBean pattern 은 immutable class 생성을 불가능하게 합니다)

Luckily, there is a third alternative that combines the safety of the telescoping constructor pattern with the readability of the JavaBeans pattern. It is a form of the Builder pattern.(다행히 telescoping constructor pattern 의 안전성과 JavaBeans pattern 의 가독성을 결합한  세 번째 대안이 있습니다. 그 것은 Builder pattern 입니다.)

"The Builder pattern simulates named optional parameters"

"Class.newInstance breaks compile-time exception checking."

"the Builder pattern is a good choice when designing classes whose constructors or static factories would have more than a handful of parameters"(생성자나 static factory 가 5개 이상의 parameter 를 갖는 class 를 설계할 때 Builder pattern 은 좋은 대안입니다.)



[항목 3] private 생성자나 enum 타입을 사용해서 싱글톤의 특성을 유지하자

http://en.wikipedia.org/wiki/Singleton_pattern#Java

public enum Elvis {
    INSTANCE;
    public void leaveTheBuilding() {...}
}

[항목 4] private 생성자를 사용해서 인스턴스 생성을 못하게 하자

[항목 5] 불필요한 객체의 생성을 피하자

[항목 6] 쓸모 없는 객체 참조를 제거하자

[항목 7] finalizer 의 사용을 피하자

알맞은 시간 안에 반드시 수행해야 하는 작업을 finalizer 에서 처리하면 절대 안 된다.

중요한 persistant 상태를 갱신하는 작업은 finalizer 에서 처리하지 말아야 한다.

명시적인 termination method 를 제공하고,

명시적인 termination method 는 즉각적인 종결처리를 위해 try-finally 구문과 함께 쓰는 것이 좋다.

CHAPTER 3 모든 객체에 공통적인 method

[항목 8] equals method 를 overriding 할 때는 보편적 계약을 따르자

각 instance 가 원래부터 유일한 경우, logical equality 검사가 큰 의미가 없는 경우, 상위 class 에서 이미 적절하게 equals 를 overriding 했고 이 것을 그대로 쓰면 되는 경우, private 이거나 package-private class 이고 equals 가 절대 호출되지 않는 경우라면 equals 를 overriding 하지 않아도 된다.

instance 를 만들 수 있는 class 를 상속받아 새로운 부분을 추가할 때, 표준 구현 계약을 준수하는 equals method 를 만들 수 있는 간단한 방법은 없다.

좋은 equals method 를 만드는 방법: 1 == 연산자를 사용하여 this 인지 검사한다. 2 instanceof 연산자를 사용하여 타입이 올바른지 검사한다. 3 인자를 정확한 타입으로 변환한다. 4 significant field 에 대해 인자의 필드와 this 의 해당 필드의 값이 동등한지 검사한다. 5 equals method 를 다 만들었다면, 대칭성, 매개성, 일관성을 지키는지 확인한다.

equals method 를 overriding 하면 hashCode method 도 반드시 overriding 하라.

너무 영리하게 굴지 마라.

불안정한 자원에 의존하는 equals method 를 작성하지 마라.

equals method 의 인자를 Object 타입이 아닌 다른 타입으로 선언하지 마라.

[항목 9] equals method 를 overriding 할 때는 hashCode method 도 항상 같이 overriding 하자

equals method 를 overriding 하고도 hashCode method 를 overriding 하지 않으면 Object.hashCode 의 표준 구현 계약을 어기는 것이다.

hashCode method 를 overriding 하지 않으면 두 번째 항목을 위반하기 쉽다. 논리적으로 동등한 object 는 반드시 hash code 가 같아야 한다.

hash code 를 계산할 때 단지 성능을 향상시킨다는 이유로 주요 필드 중 하나라도 계산에서 빼먹지 마라.

[항목 10] toString method 는 항상 overriding 하자

toString method 를 잘 구현해 놓은 class 는 쓰기 훨씬 편할 것이다.

toString method 는 object 가 가진 관심 있는 모든 정보를 return 해야 한다.

문자열 형식을 명세화하지 않더라도 toString method 의 의도는 명확히 문서화하는 것이 좋다.

toString method 가 return하는 문자열에 포함된 모든 정보에 접근할 수 있는 방법을 제공하는 것이 좋다.

[항목 11] clone method 는 신중하게 overriding 하자

final 이 아닌 class 에서 clone method 를 overriding 하려면 반드시 super.clone method 를 호출하여 얻은 object 를 return 해야 한다.

실제로, Clonable interface 를 implements 하는 class 는 제대로 동작하는 public clone method 를 제공해야 한다.

본질적으로 clone method 는 또 다른 종류의 생성자나 마찬가지다. clone method 가 생성하는 복제본 object 는 class 의 invariant 를 지켜야 한다.

근본적으로 객체의 복제본을 만들어야 하는 clone 아키텍처는 final 필드로 가변 객체를 참조하는 클래스와 같이 쓸 수 없다.

복제본을 만드는 것을 허용하지 않거나 복제본을 얻을 수 있는 더 나은 다른 방법을 제공해야 한다.

object 를 복제하는 또 다른 좋은 방법은 copy constructor 를 제공하는 것이다.

[항목 12] Comparable interface 의 구현을 고려하자

CHAPTER 4 class 와 interface

[항목 13] class 와 그 member 의 접근성을 최소화하자

가장 중요한 법칙은 class 나 class member 에 대한 접근은 가능하면 최소화 하라는 것이다.

public static final 필드인 배열은 거의 확실하게 문제를 일으킨다.

[항목 14] public 클래스에서는 public 필드가 아닌 접근자(accessor) 메소드를 사용한다

[항목 15] 가변성을 최소화 하자

[항목 16] 가급적 inheritance 보다는 composition 을 사용하자

method 호출과 달리 inheritance 는 encapsulation 규칙을 어긴다.

[항목 17] inheritance 을 위한 설계와 문서화를 하자. 그렇지 않다면 inheritance 의 사용을 금지시킨다

class 명세 문서에 method 를 overriding 하면 발생할 수 있는 모든 파급 효과를 자세하고 정확하게 기술해야 한다.

protected method 를 제공하여 이 class 의 내부 동작에 접근할 수 있는 연결 고리를 제공해야 할 지도 모른다.

직접이든 간접이든, 생성자에서 재정의 가능한 method 를 절대로 호출해선 안 된다.

clone  이나 readObject method 에서 overriding 가능한 method 를 절대로 호출하면 안 된다.

readResolve 나 writeResolve method 를 제공한다면 반드시 이 method 들을 private 이 아니 protected 로 만들어야 한다.

inheritance 를 위해 class 를 설계하려면 이 class 에 실제로 제약을 줄 수 밖에 없다.

inheritance 와 관련된 문제를 해결하는 가장 좋은 방법은 안전하게 inheritance 할 수 있도록 설계하지도 않고 문서화 하지도 않은 class 는 아예 inheritance 가 불가능하게 만드는 것이다.

[항목 18] abstract class 보다는 interface 를 사용하자

이미 존재하는 class 가 새로운 interface 를 구현하도록 고치는 것은 어려운 일이 아니다.

interface 를 쓰면 깔끔하게 mixin 타입을 정의할 수 있다.

interface 를 쓰면 계층 구조가 없는 타입 프레임워크를 만들 수 있다.

interface 는 안전하고 강력하게 클래스에 새로운 기능을 더할 수 있다.

interface 와 abstract class 의 장점만 결합해서, 외부에 제공하는 중요한 인터페이스들에 대해 skeleton inplementation 을 해 놓은 abstract class 를 제공할 수 있다.

interface 를 발전시켜 가는 것보다 abstract class 를 발전시켜 가는 것이 훨씬 더 쉽다는 것이다.

[항목 19] 타입을 정의할 때만 interface 를 사용하자

[항목 20] 태그(tagged) 클래스보다는 클래스 계층을 사용하자

[항목 21] 전략을 표현할 때 함수 객체를 사용하자

[항목 22] static 멤버 클래스를 많이 사용하자

CHAPTER 5 제네릭(Generics)

[항목 23] 새로 작성하는 코드에서는 원천(raw) 타입을 사용하지 말자

[항목 24] 컴파일 경고 메시지가 없게 하자

[항목 25] 배열보다는 List를 사용하자

[항목 26] 제네릭 타입을 애용하자

[항목 27] 제네릭 메소드를 애용하자

[항목 28] 바운드 와일드 카드를 사용해서 API의 유연성을 높이자

[항목 29] 타입 안전이 보장되는 혼성(heterogeneous) 컨테이너의 사용을 고려하자

CHAPTER 6 열거형(Enum)과 주석(Annotation)

[항목 30] int 상수 대신 enum을 사용하자

[항목 31] 서수(序數) 대신 인스턴스 필드를 사용하자

[항목 32] 비트(bit) 필드 대신 EnumSet을 사용하자

[항목 33] 서수(序數) 인덱스 대신 EnumMap을 사용하자

[항목 34] 인터페이스를 사용해서 확장 가능한 enum을 만들자

[항목 35] 작명 패턴보다는 주석(annotation)을 사용하자

[항목 36] Override 주석을 일관성 있게 사용하자

[항목 37] 타입 정의는 표시 인터페이스를 사용하자

CHAPTER 7 method

[항목 38] parameter 가 유효한지 검사하자

[항목 39] 필요하면 defensive copy 를 만들자

class 의 모든 client 는 항상 불변 규칙을 깨뜨리기 위해 최선을 다한다고 가정하고, 프로그래밍할 때 항상 방어하는 자세를 가져야 한다.

생성자에 전달되는 변경가능 인자들을 방어복사해야 하고

인자의 유효성을 검사하기 전에 먼저 복사하고 나서 우너본이 아닌 복사본의 유효성을 검사한다

믿을 수 없는 하위 클래스가 생길 수 있는 인자들에 대한 방어복사에 clone method 를 쓰지 말아야 한다.

[항목 40] method signiture 를 신중하게 설계하자

method 이름을 신중하게 결정하라.

편리한 method 를 제공하기 위해 너무 애쓰지 마라.

조금이라도 의심이 생기면 "속기" method 를 그냥 제거하라.

인자를 너무 많이 받지 마라.

인자의 타입으로  class 보다 interface 를 써라.

function object 는 신중하게 써라.

[항목 41] overloading 을 분별력 있게 사용하자

overloading method 중에서 어떤 것을 호출할지 고르는 것은 컴파일 시점에 결정난다.

overloading method 선택은 정적으로 결정되고, overriding method 선택은 동적으로 결정난다.

overloading method 가 혼란을 주면 안된다.

안전하고 보수적인 정책은 같은 개수의 인자를 갖는 overloading method 를 만들지 않는 것이다.

[항목 42] 가변 인자(varargs)를 분별력 있게 사용하자

[항목 43] null대신 비어있는 배열이나 컬렉션을 반환하자

배열을 return 하는 method 에서 null 을 return 할 이유가 전혀 없다. null 을 return 할 상황이라면 길이가 0 인 배열을 return 하면 된다.

[항목 44] 외부에 제공하는 모든 API 요소에 대해 문서화 주석을 넣자

API 를 문서화 하려면, 외부에 제공하는 모든 class, interface, 생성자 method, 필드에 문서화 주석을 달아야 한다.

method 의 문서화 주석은 method 와 client 사이의 계약을 간명하게 기술해야 한다.

CHAPTER 8 프로그래밍 일반

[항목 45] 지역 변수의 유효 범위를 최소화 하자

지역 변수의 유효 범위를 최소화 하는 가장 좋은 방법은 쓰기 바로 전에 선언하는 것이다.

대부분 지역 변수는 선언과 함께 초기화 해야 한다.

while 반복문 보다는 for 반복문을 쓰는 것이 좋다.

한 method 는 한 가지 작업만 처리하도록 가능하면 작게 만들어야 한다.

[항목 46] 종전의 for 루프보다는 for-each 루프를 사용하자

[항목 47] 라이브러리를 배우고 사용하자

표준 라이브러리를 쓰면, 이 것을 작성한 전문가의 지식과 먼저 써 본 사람들의 경험까지 얻을 수 있다.

주요 배포판이 새롭게 발표될 때마다 라이브러리에 추가되는 많은ㅇ 기능을 알아둘 필요가 있다.

적어도 java.lang.util 패키지와 조금 중요성이 떨어지기는 하지만 java.lang.io 패키지의 내용만은 반드시 알아 두어야 한다.

[항목 48] 정확한 계산에는 float나 double 타입을 쓰지 말자

특히, 금전을 계산할 때 float 나 double 타입을 절대로 쓰지 말아야 한다.

[항목 49] 박스화 기본형보다는 기본형을 사용하자

[항목 50] 다른 타입을 쓸 수 있는 곳에서는 String 사용을 피하자

String 으로 다른 value type 을 대신하지 마라.

String 으로 열거타입을 대신하지 마라. 타입안전 열거를 쓰는 것이 가장 좋다.

String 으로 aggregate type 을 대신하지 마라.

String 으로 어떤 capability 를 대신하지 마라.

[항목 51] String 결합의 성능 저하를 주의하자

n 개의 String 을 연결할 때 String 연결 연산자를 반복해서 쓰면 걸리는 시간은 n 의 제곱에 비례한다.

[항목 52] object reference 는 그 object 의 interface 타입으로 하자

적절한 interface 타입이 있다면 인자, return 값, 변수, 필드는 반드시 interface 타입으로 선언해야 한다.

object reference 의 타입을 interface 타입으로 선언하면 유연한 프로그램을 만들 수 있다.

적절한 interface 가 없다면, interface 타입이 아닌 class 타입으로 object 를 참조해도 괜찮다.

[항목 53] 리플렉션보다는 interface 를 사용하자

reflection 을 쓰면 예외에 대한 검사를 포함하여, 모든 컴파일 시점의 타입 검사를 포기해야 한다. reflection 을 쓰면 코드가 매우 번거로워지고 양도 많아진다. 성능이 떨어진다.

일반 응용 프로그램에서 실행 시점에 reflection 을 써서 object 에 접근하면 안 된다.

아주 제한된 형태로 reflection 을 쓴다면 거의 비용을 들이지 않고 reflection 의 많은 장점만 활용할 수 있다.

reflection 으로 현재 존재하지 않는 class 의 instance 를 생성하고, interface 나 상위 class 로 이 instance 에 접근하는 방식을 쓸 수 있다. 

[항목 54] native method 를 분별력 있게 사용하자

1.3 부터 native method 를 써서 성능을 높이는 것을 권장하지 않는다.

[항목 55] 잘 판단해서 최적화하자

무지를 비롯한 다른 어던 이유보다 효율성(실제 달성하지도 못하면서)이란 명목으로 모든 전산 작업의 죄악은 저질러진다 - William A. Wulf

사소한 효율성은 잊어야 한다. 나머지 97%의 시간을 이야기하자. 성급한 최적화는 모든 죄악의 근원이다. - Donald E. Knuth

최적화에는 다음과 같은 두 가지 규칙이 있다.
규칙 1. 하지 마라.
규칙 2 (전문가만 해당함). 아직은 하지 마라. 최적화가 필요하다는 것을 100% 확신하지 못한다면 하지 마라. - M. A. Jackson

빠른 프로그램보다는 좋은 프로그램을 만들기 위해 노력하라.

성능을 제한할 수 있는 설계를 하지 마라.

API 에 대한 설계 결정이 성능에 미치는 영향을 고려해야 한다.

성능을 높이기 위해 API 를 한 번 더 감싸는 것은 아주 나쁜 생각이다.

최적화 전후의 성능을 측정하여 비교하라.

[항목 56] 보편화된 작명 규칙을 따르자

CHAPTER 9 Exceptions

[항목 57] 예외 상황에서만 exception 을 사용하자

exception 은 말 그대로 예외상황에서만 써야 한다.프로그램 흐름을 exception 으로 제어하려 하면 안 된다.

좋은 API 는 클라이언트가 프로그램 흐름을 제어할 때 Exception 을 쓸 수 밖에 없도록 만들지 않는다.

[항목 58] 복구 가능 상황에는 checked exception 을 사용하고 runtime exception 은 프로그램 에러에 사용하자

checked exception 은 호출자가 예외상황을 복구할 수 있다고 기대할 수 있을 때 던진다.

runtime exception 은 프로그래밍 오류가 발생했을 때만 써야 한다.

처리하지 않는 exception 은 모두 RuntimeException 의 하위 class 여야 한다.

[항목 59] checked exception 의 불필요한 사용을 피하자

[항목 60] 표준 exception 을 사용하자

[항목 61] 하위 계층의 예외 처리를 신중하게 하자

[항목 62] method 가 던지는 모든 exception 을 문서화하자

처리해야 하는 exception 은 method 선언부에 하나씩 선언하고, @throws 태그를 써서 모든 exception 이 발생하는 상황을 정확하게 문서화하라.

unchecked exception 은 @throws 태그를 써서 명세 문서에 기술하지만, method 선언의 throws 절에는 나타나지 말아야 한다.

대부분 method 가 같은 exception 을 던지는 class 라면, 이런 예외는 각 method 의 문서화 주석에 기술하는 것보다 클래스의 문서화 주석에 기술하는 것이 좋다.

[항목 63] 실패 상황 정보를 상세 메시지에 포함하자

실패 원인을 포착하려면, exception 의 문자열 표현에 반드시 exception 발생에 영향을 준 모든 필드와 인자의 값이 들어 있어야 한다.

[항목 64] 실패 원자성을 갖도록 노력하자

method 호출이 실패하더라도 object 상태는 method 호출 전과 같아야 한다.

[항목 65] exception 을 묵살하지 말자

빈 catch 블록은 "exception 을 처리하라"라고 알려주는 exception 의 존재 이유 자체를 짓밟는 것이다.

catch 블록 안에서 정말 아무 것도 할 것이 없다면 최소한 왜 exception 을 잡아서 처리하지 않고 버리는지 그 이유라도 주석으로 달아 놓아야 한다.

CHAPTER 10 동시성(Concurrency)

[항목 66] 공유하는 가변 데이터에 접근 시 동기화하자

혹시 성능을 향상시키려고 더 이상 쪼개지지 않는 일기/쓰기 작업에 동기화를 쓰지 말아야 한다는 이야기를 들었을지 모르지만, 이것은 매우 위험한 생각이다.

동기화는 상호배제뿐만 아니라, thread 끼리 믿을 수 있게 변수의 값을 전달할 때도 꼭 필요한 작업이다.

이중확인 구현패턴은 제대로 동작하지 않는다.

여러 thread 가 변경 가능한 데이터를 공유하려면 읽고 쓸 때 반드시 잠금장치를 얻어야 한다.

[항목 67] 지나친 동기화는 피하자

교착 상태에 빠지지 않으려면 동기화 method 는 동기화 블록 안에서 클라이언트에게 제어를 절대 넘겨서는 안 된다.

동기화 영역 안에서는 작업을 조금만 처리하는 것을 원칙으로 삼아야 한다.

[항목 68] 스레드 그룹보다는 실행자와 작업을 사용하자

[항목 69] wait와 notify 대신 동시성 유틸리티를 사용하자

[항목 70] 스레드 안전을 문서화 하자

[항목 71] 늦 초기화를 분별력 있게 사용하자

[항목 72] thread 스케줄러에 의존하지 말자

프로그램의 정확성과 성능을 thread 스케줄러에 의존하면 이식성이 없어질 확률이 커진다.

Thread.yield method 를 써서 이런 문제를 억지로 뜯어 고치려 하지 마라.

Java 플랫폼에서 이식성이 가장 떨어지는 것 중 하나가 바로 thread 우선 순위이다.

Thread.yield method 로 얻을 수 있는 것이라고는 프로그램을 시험해 보기 위해 일부러 동시 접근을 늘릴 수 있다는 것 밖에 없다.

[항목 73] thread 그룹을 사용하지 말자

thread 그룹은 폐물이나 다름 없다.

CHAPTER 11 직렬화(Serialization)

[항목 74] Serializable interface 를 분별력 있게 구현하자

어떤 class 가 Serializable interface 를 implements 하고 나면, 일단 이 class 가 배포된 후에 구현을 바꾸는 데 드는 비용이 매우 커진다.

Serializable interface 를 implements 할 때 두 번째로 고려해야 할 것은 버그와 보안 구멍이 생길 가능성이 커진다는 것이다.

Serializable interface 를 implements 할 때 세 번째로 고려해야 하는 것은, 새 버전의 class 를 배포할 때 시험해야 할 것이 더 많아진다는 것이다.

Serializable interface 를 implements 하는 것은 쉽게 결정할 문제가 아니다.

inheritance 를 위해 설계한 class 와 interface 는 Serializable interface 를 implements, extends 하지 않는 것이다.

inheritance 를 위한 class 가 직렬화를 지원하지 않기로 했다면, 접근할 수 있는 기본 생성자를 제공하는 것이 좋다.

inner class 는 Serializable interface 를 implements 하지 말 것을 강력하게 권고한다.

inner class 의 기본 직력화 형태에 대한 명확한 정의가 없다는 것이다.

[항목 75] 독자적인 직렬화 형태의 사용을 고려하자

기본 직렬화 형태를 쓰기로 결정하기 전에 반드시 이것이 올바른 선택인지 다시 한 번 생각해 보아야 한다.

object 의 논리적 내용과 물리적 표현이 같다면 기본 직렬화 형태를 써도 좋다.

기본 직렬화 형태를 쓰더라도, readObject method 를 제공하여 불변규칙과 보안을 확실히 보장해야 한다.

object 의 물리적 표현이 논리적 내용과 차이가 많이 날 때 기본 직렬화 형태를 쓰면 다음과 같은 네 가지 문제가 생긴다. 외부에 제공하는 API 가  class 내부 상세 구현에 영원히 얽매이게 된다. 많은 저장공간이 필요할 수 있다. 성능이 떨어진다. 스택 오버플로우를 일으킬 수 있다.

모든 instance 필드가 transient 라면 defaultWriteObject 와 defaultReadObject method 를 호출하지 않아도 문제는 없지만 그래도 호출하는 것이 좋다.

transient 가 아닌 필드를 만들 때는 항상 이 필드의 값이 객체의 논리적 내용의 일부인지 다시 한 번 확인해야 한다.

기본 직렬화 형태를 쓰던 맞춤 직렬화 형태를 쓰던 직렬화를 제공하는 모든 class 는 Serial Version UID 를 명시적으로 제공하는 것이 좋다.

[항목 76] 방어 가능한 readObject 메소드를 작성하자

object 를 역직렬화할 때, 클라이언트가 절대 얻어서는 안되는 object 를 참조하는 필드들은 반드시 방어복사해야 한다.

[항목 77] 인스턴스 제어에는 readResolve 메소드보다 enum 타입을 사용하자

[항목 78] 직렬화된 인스턴스 대신 직렬화 프록시의 사용을 고려하자


http://mindasom.tistory.com2009-11-17T12:57:270.31010