You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// 기본 직렬화에 적합publicclassNameimplementsSerializable {
privatefinalStirnglastName;
privatefinalStringfirstName;
privatefinalStringmiddleName;
}
기본 직렬화가 적합하다고 결정해도 불변식 보장과 보안을 위해 readObject 메서드를 제공해야 할 때가 많다.
기본 직렬화 문제점 (객체의 물리적 표현과 논리적 내용이 같은 다른 경우)
// 기본 직렬화에 적합하지 않음 (객체의 물리적 표현과 논리적 내용이 같은 다른 경우)// - 논리적으로는 일련의 문자열을 표현, 물리적으로는 이중 연결 리스트로 연결// - 기본 직렬화 사용하면 연결정보 포함된 모든 엔트리를 기록publicfinalclassStringListimplementsSerializable {
privateintsize = 0;
privateEntryhead = null;
privatestaticclassEntryimplementsSerializable {
Stringdata;
Entrynext;
Entryprevious;
}
// ... 생략
}
공개API가 현재의 내부 표현 방식에 영구히 묶인다.
연결리스트를 더 이상 사용하지 않더라도 관련 코드를 제거할 수 없다.
너무 많은 공간을 차지할 수 있다.
앞의 예에서 내부 구현에 해당하는 정보까지 모두 직렬화 형태에 포함됨
네트워크 전송 속도만 느려짐
시간이 너무 많이 걸릴 수 있다.
직렬화 로직은 객체 그래프의 위상에 관한 정보를 알 수 없으니, 직접 순회할 수 밖에 없음
스택 오버플로를 일으킬 수 있다.
기본 직렬화 과정은 객체 그래프를 재귀 순회하는데, 스택 오버플로 일어킬 수도 있다.
합리적 직렬화 형태 (StringList)
물리적 상세 표현은 배제한 채 논리적인 구성만 담는다.
publicfinalclassStringListimplementsSerializable {
privatetransientintsize = 0;
privatetransientEntryhead = null;
// 이제는 직렬화되지 않는다.privatestaticclassEntry {
Stringdata;
Entrynext;
Entryprevious;
}
// 지정한 문자열을 이 리스트에 추가한다.publicfinalvoidadd(Strings) { }
/** * 이 {@code StringList} 인스턴스를 직렬화한다. * * @serialData 이 리스트의 크기(포함된 문자열의 개수)를 기록한 후 * ({@code int}), 이어서 모든 원소를(각각은 {@code String}) * 순서대로 기록한다. */privatevoidwriteObject(ObjectOutputStreams)
throwsIOException {
s.defaultWriteObject();
s.writeInt(size);
// 모든 원소를 올바른 순서로 기록한다.for (Entrye = head; e != null; e = e.next)
s.writeObject(e.data);
}
privatevoidreadObject(ObjectInputStreams)
throwsIOException, ClassNotFoundException {
s.defaultReadObject();
intnumElements = s.readInt();
// 모든 원소를 읽어 이 리스트에 삽입한다.for (inti = 0; i < numElements; i++)
add((String) s.readObject());
}
// 나머지 코드는 생략
}
writeObject와 readObject가 직렬화 형태를 처리한다.
transient 한정자는 해당 인스턴스 필드가 기본 직렬화 형태에 포함되지 않는다는 표시다.
여기서 defaultWriteObject() 메서드는 기본 직렬화하는데 포함 안된다.
해당 객체의 논리적 상태와 무관한 필드라고 확신할 때만 transient 한정자를 생략해야한다.
기본 직렬화를 하면 transient 필드들은 역직렬화 할때 기본값으로 초기화 된다.
serialVersionUID
어떤 직렬화 형태를 택하든 직렬화 가능 클래스 모두에 직렬 버전 UID를 명시적으로 부여하자.
잠재적 호환성 문제 사라짐 (아이템 86참조)
// 무작위로 고른 long 값 (고유할 필요 없음)privatestaticfinallongserialVersionUID = 234L
구버전으로 직렬화된 인스턴스들과의 호환성을 끊으려는 경우를 제외하고는 직렬 버전 UID를 절대 수정하지 말자.
요약
기본 직렬화 형태는 객체를 직렬화한 결과가 해당 객체의 논리적 표현에 부합할 때만 사용하고, 아니면 적절히 설명하는 커스텀 직렬화 형태를 고안하라