We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
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
개발 일정에 쫓기는 상황이라면 현재 릴리스에서는 그냥 동작만 하도록 만들고, 다음 릴리스에서 제대로 구현한다. 그리고 보통은 크게 문제되지는 않는다.
하지만 Serializable을 구현하고 기본 직렬화 형태를 사용하게 되면 다음 릴리스 때 수정하려고 한 현재의 구현 형태에 발이 묶이게 된다. (= 기본 직렬화 형태를 수정하기 어려워 진다.)
따라서 먼저 고민해보고 괜찮다고 판단될 때만 기본 직렬화 형태를 사용해야 한다.
객체의 물리적 표현과 논리적 내용이 같다면 기본 직렬화 형태를 적용해도 괜찮다.
public class Name implements Serializable { /** * 성, null이 아니어야 함 * @serial */ private final String lastName; /** * 이름, null이 아니어야 함 * @serial */ private final String firstName; /** * 중간 이름, 중간 이름이 없는 경우 null * @serial */ private final String middleName; ... }
이름은 이름, 성, 중간 이름이라는 3개의 문자열로 구성되고, 각 필드는 논리적 내용을 그대로 반영한다.
기본 직렬화 형태가 적합하다고 결정했더라도 불변식 보장과 보안을 위해 readObject 메서드를 제공해야 할 때가 많다.
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; } ... }
위의 코드는 논리적으로 일련의 문자열을 표현하고, 물리적으로는 문자연들을 이중 연결 리스트로 연결한다.
객체의 물리적 표현과 논리적 표현의 차이가 클 때 기본 직렬화 형태를 사용하면 크게 네 가지 면에서 문제가 생긴다.
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) { ... } /** * {@code StringList} 인스턴스를 직렬화한다. * * @serialData 이 리스트의 크기(포함된 문자열의 개수)를 기록한 후 * ({@code int}), 이어서 모든 원소를(각각은 {@code string}) 순서대로 기록한다. */ private void writeObject(ObjectOutputStream s) throws IOException { s.defaultWriteObject(); s.writeInt(size); // 모든 원소를 올바른 순서로 기록한다. for (Entry e = head; e != null; e = e.next) s.writeObject(e.data); } private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); int numElements = s.readInt(); // 모든 원소를 읽어 이 리스트에 삽입한다. for (int i = 0; i < numElements; i++) add((String) s.readObject()); } ... }
Stringlist의 필드 모두가 transient더라도 writeObject와 readObject는 각각 가장 먼저 defaultWriteObject와 defaultReadObject를 호출한다.
기본 직렬화를 수용하든 하지 않든 defaultWriteObject 메서드를 호출하면 transient로 선언하지 않은 모든 인스턴스 필드가 직렬화된다. 따라서 transient로 선언해도 되는 인스턴스 필드에는 모두 transient 한정자를 붙여야 한다.
기본 직렬화를 사용한다면 transient 필드들은 역직렬화될 때 기본값으로 초기화됨을 잊지 말아야 한다.
기본 직렬화 사용 여부와 상관없이 객체의 전체 상태를 읽는 매서드에 적용 해야 하는 동기화 메커니즘을 직렬화에도 적용해야 한다.
// 기본 직렬화를 사용하는 동기화된 클래스를 위한 writeObject 매서드 private synchronized void writeObject(ObjectOutputStream s) throws IOException { s.defaultWriteObject() ; }
모든 메서드를 synchronized로 선언해서 스레드 안전한 객체에서 기본 직렬화를 사용하기 위해서는 메서드도 writeObject도 위의 코드처럼 synchronized로 선언해줘야 한다.
private static final long serialVersionUID = <무작위로 고른 long 값>;
어떤 직렬화 형태를 택하든 직렬화 가능 클래스 모두에 직렬 버전 UID를 명시적으로 부여해야 한다.
기본 버전 클래스와의 호환성을 끊고 싶다면 직렬 버전 UID를 바꿔주면 된다.
The text was updated successfully, but these errors were encountered:
ruthetum
No branches or pull requests
커스텀 직렬화 형태를 고려해보라
개발 일정에 쫓기는 상황이라면 현재 릴리스에서는 그냥 동작만 하도록 만들고, 다음 릴리스에서 제대로 구현한다. 그리고 보통은 크게 문제되지는 않는다.
하지만 Serializable을 구현하고 기본 직렬화 형태를 사용하게 되면 다음 릴리스 때 수정하려고 한 현재의 구현 형태에 발이 묶이게 된다. (= 기본 직렬화 형태를 수정하기 어려워 진다.)
따라서 먼저 고민해보고 괜찮다고 판단될 때만 기본 직렬화 형태를 사용해야 한다.
기본 직렬화 형태에 적합한 경우
객체의 물리적 표현과 논리적 내용이 같다면 기본 직렬화 형태를 적용해도 괜찮다.
이름은 이름, 성, 중간 이름이라는 3개의 문자열로 구성되고, 각 필드는 논리적 내용을 그대로 반영한다.
기본 직렬화 형태가 적합하다고 결정했더라도 불변식 보장과 보안을 위해 readObject 메서드를 제공해야 할 때가 많다.
기본 직렬화 형태에 적합하지 않은 경우
위의 코드는 논리적으로 일련의 문자열을 표현하고, 물리적으로는 문자연들을 이중 연결 리스트로 연결한다.
객체의 물리적 표현과 논리적 표현의 차이가 클 때 기본 직렬화 형태를 사용하면 크게 네 가지 면에서 문제가 생긴다.
합리적인 커스텀 직렬화 형태를 갖춘 StringList
Stringlist의 필드 모두가 transient더라도 writeObject와 readObject는 각각 가장 먼저 defaultWriteObject와 defaultReadObject를 호출한다.
기본 직렬화를 수용하든 하지 않든 defaultWriteObject 메서드를 호출하면 transient로 선언하지 않은 모든 인스턴스 필드가 직렬화된다. 따라서 transient로 선언해도 되는 인스턴스 필드에는 모두 transient 한정자를 붙여야 한다.
기본 직렬화를 사용한다면 transient 필드들은 역직렬화될 때 기본값으로 초기화됨을 잊지 말아야 한다.
동기화
기본 직렬화 사용 여부와 상관없이 객체의 전체 상태를 읽는 매서드에 적용 해야 하는 동기화 메커니즘을 직렬화에도 적용해야 한다.
모든 메서드를 synchronized로 선언해서 스레드 안전한 객체에서 기본 직렬화를 사용하기 위해서는 메서드도 writeObject도 위의 코드처럼 synchronized로 선언해줘야 한다.
직렬 버전 UID
어떤 직렬화 형태를 택하든 직렬화 가능 클래스 모두에 직렬 버전 UID를 명시적으로 부여해야 한다.
기본 버전 클래스와의 호환성을 끊고 싶다면 직렬 버전 UID를 바꿔주면 된다.
정리
The text was updated successfully, but these errors were encountered: