Serializable
을 구현하고 기본 직렬화 형태를 사용한다면 현재의 구현에 종속적이게 된다. 즉, 기본 직렬화 형태를 버릴 수 없게 된다. [코드 87-1] 기본 직렬화 형태에 적합한 후보
public class Name implements Serializable {
/**
* 성. null이 아니어야 함.
* @serial
*/
private final Stirng lastName;
/**
* 이름. null이 아니어야 함.
* @serial
*/
private final String firstName;
/**
* 중간이름. 중간이름이 없다면 null.
* @serial
*/
private final String middleName;
... // 나머지 코드는 생략
}
readObject
메서드를 제공해야 하는 경우가 많다.readObject
메서드가 보장해야 한다.[코드 87-2] 기본 직렬화 형태에 적합하지 않은 클래스
public final class StringList implements Serializable {
private int size = 0;
private Entry head = null;
private static class Entry implements Serializable {
String data;
Entry next;
Entry previous;
}
... // 나머지 코드는 생략
}
StringList를 위한 합리적인 직렬화 형태는 무엇일까?
[코드 87-3] 합리적인 커스텀 직렬화 형태를 갖춘 StringList
public final class StringList implements Serializable {
private transient int size = 0;
private transient Entry head = null;
// 이번에는 직렬화 하지 않는다.
private static class Entry {
String data;
Entry next;
Entry previous;
}
// 지정한 문자열을 리스트에 추가한다.
public final void add(String s) { ... }
/**
* StringList 인스턴스를 직렬화한다.
*/
private void writeObject(ObjectOutputStream stream)
throws IOException {
stream.defaultWriteObject();
stream.writeInt(size);
// 모든 원소를 순서대로 기록한다.
for (Entry e = head; e != null; e = e.next) {
s.writeObject(e.data);
}
}
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
int numElements = stream.readInt();
for (int i = 0; i < numElements; i++) {
add((String) stream.readObject());
}
}
... // 나머지 코드는 생략
}
transient
키워드가 붙은 필드는 기본 직렬화 형태에 포함되지 않는다. transient
로 선언되었더라도 writeObject
와 readObject
메서드는 각각 defaultWriteObject
와 defaultReadObject
메서드를 호출한다. transient
가 아닌 필드가 추가되더라도 상위와 하위 모두 호환이 가능하기 때문이다.readObject
메서드에서 defaultReadObject
를 호출하지 않는다면 역직렬화 과정에서 StreamCorruptedException
이 발생한다.defaultWriteObject
메서드를 호출하면 transient
로 선언하지 않은 모든 필드는 직렬화된다. transient
키워드를 선언해도 되는 인스턴스 필드에는 모두 붙여주자. transient
한정자를 생략해야 한다.transient
필드는 기본값으로 초기화된다. readObject
메서드에서 defaultReadObject
메서드를 호출한 다음 원하는 값으로 지정하면 된다. synchronized
로 선언하여 스레드 안전하게 만든 객체에 기본 직렬화를 사용한다면, writeObject
도 아래처럼 수정해야 한다.private synchronized void writeObject(ObjectOutputStream stream)
throws IOException {
stream.defaultWriteObject();
}
SerialVersionUID
(이하 SUID)를 명시적으로 선언해야 한다.private static final long serialVersionUID = <무작위로 고른 long 값>;