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을 구현하는 클래스를 시간이 없다고 기본 직렬화 형태를 사용한다면 다음 릴리즈 때 버리려 한 현재의 구현에 영원히 발이 묶이게 된다.
실제로도 BigInteger 같은 클래스는 이 문제에 시달리고 있다.
객체의 물리적 표현과 논리적 내용이 같다면 기본 직렬화 형태라도 무방하다.
적합한 예시
public class Name implements Serializable{ private final String lastName; private final String firstName; private final String middleName; ... }
적합하지 않은 예시
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; } }
만약 객체의 물리적 표현과 논리적 표현의 차이가 클 경우, 기본 직렬화 형태를 사용하?
공개 API가 현재의 내부 표현 방식에 영구히 묶인다.
Entry가 공개가 되어버린다.
다른 릴리즈에서 내부 표현 방식을 바꾸더라도 StringList는 여전히 연결 리스트로 표현된 입력도 처리할 수 있어야 함
= 연결리스트를 더는 사용하지 않아도 관련 코드를 지울 수 없음
너무 많은 공간을 차지할 수 있다.
Entry와 연결 정보는 사실 내부 구현에 해당하므로 직렬화에 포함할 가치가 없다.
이는 저장, 통신 속도 저하로 이어진다.
시간이 너무 많이 걸릴 수 있다.
객체 그래프의 위상에 대한 정보가 없어 직접 그래프를 순회해볼 수 ㅏㅂㄲ에 없다.
스택 오버플로우를 일으킬 수 있다.
StringList는 무자열을 나열하는 정도의 직렬화면 충분할 것 같다.
= 물리적인 상세 표현은 배제한 채 논리적인 구성만 담는 것이다.
private 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; } //직렬화 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()); } } ... }
transient : 일시적이다. 해당 인스턴스 필드가 기본 직렬화 형태에 포함되지 않는다.
transient여도 writeObject와 readObject로 defaultWriteObject, defaultReadObject를 호출하는 작업은 꼭 필요하다. → 향후 transient가 아닌 인스턴스 필드가 추가되더라도 호환된다.
ex) 신버전 인스턴스를 직렬화 한 후 구버전으로 역직렬화하면 새로 추가된 필드들은 무시될 것이다.
하지만 defaultReadObject가 호출되지 않으면 역직렬화에서 StreamCorruptedException이 발생할 것이다.
이전보다 개선 버전의 StringList는 문자열의 길이가 평균 10일 때 공간 절반, 속도 두 배가 된다.
또한 스택 오버플로가 발생하지 않는다.
StringList보다 더 심한 클래스도 있다.
유연성과 성능이 떨어졌을 뿐이지 객체를 직렬화 한 후 역직렬화 하면 원래 객체르 륵 불변식가지 포함해 제대로 복원해내긴 한다. → 정확한 객체
하지만 그 불변식이 세부 구현에 따라 달라지는 객체가 있다.
ex) 해시테이블
물리적으로는 키-값 엔트리를 나열한 형태
논리적으로는 어떤 엔트리를 어떤 버킷에 담을지 키에서 구한 해시코드가 결정함. 그 계산 방식은 구현에 따라, 계산할 때마다 달라지기도 함
→ 해시테이블에 기본 직렬화 = 심각한 버그
기본 직렬화를 수용하든 하지않든 defaultWriteObject 메서드를 호출하면 transient로 선언하지 않은 모든 인스턴스 필드가 직렬화된다.
따라서 transient로 선언해도 되는 인스턴스 필드에서는 모두 transient 한정자를 붙여야 한다.
등이 이에 해당한다.
해당 객체의 논리적 상태와 무관한 필드라고 확신할 대만 transient 한정자를 생략하자.
→ 커스텀 직렬화에서는 대부분의 인스턴스 필드를 transient로 선언해야 한다.
기본 직렬화를 사용할 때 transient 필드들은 역직렬화될 때 기본값(null, false, 0)으로 초기화 된다.
기본값을 그대로 사용해서는 안된다면 defaultReadObject에서 원하는 값으로 복원하거나, 처음 사용할 때 초기화 하자(Item 83)
기본 직렬화 사용 여부와 관계 없이 객체의 전체 상태를 읽는 메서드에 적용해야 하는 동기화 매커니즘을 직렬화에도 사용하라.
모든 메서드를 syncronized로 선언하여 스레드 안전하게 만든 객체(Item 82) 에서 기본 직렬화를 사용하려면 writeObject도 syncronized 선언하라.
private syncronized void writeObject(ObjectOutputStream s) throws IOException { s.defaultWriteObject(); }
writeObject 메서드 안에서 동기화하고 싶다면 클래스의 다른 부분에서 사용하는 락 순서를 똑같이 따라야 한다. → 자원순서교착상태
어떤 직렬화 형태를 택하든 직렬화 가능 클래스 모두에 직렬버전 UID를 명시적으로 부여하자.
private static fianl long serialVersionUID = [무작위값];
고유할 필요 없다.
기존 버전 클래스와 호환성을 끊고 싶다면 단순히 직렬 버전 UID의 값을 바꿔줘라
하지만 이런 이유가 아니면 절대 수정하지 마라. → InvaildClassException
The text was updated successfully, but these errors were encountered:
yejin9858
No branches or pull requests
개발 일정에 쫒기는 상황에서는 그냥 동작만 하게 만들어놓는 방식 좋다.
하지만 Serializable을 구현하는 클래스를 시간이 없다고 기본 직렬화 형태를 사용한다면 다음 릴리즈 때 버리려 한 현재의 구현에 영원히 발이 묶이게 된다.
실제로도 BigInteger 같은 클래스는 이 문제에 시달리고 있다.
먼저 고민해보고 괜찮다고 판단될 때만 기본 직렬화 형태를 사용하라.
객체의 물리적 표현과 논리적 내용이 같다면 기본 직렬화 형태라도 무방하다.
적합한 예시
적합하지 않은 예시
만약 객체의 물리적 표현과 논리적 표현의 차이가 클 경우, 기본 직렬화 형태를 사용하?
공개 API가 현재의 내부 표현 방식에 영구히 묶인다.
Entry가 공개가 되어버린다.
다른 릴리즈에서 내부 표현 방식을 바꾸더라도 StringList는 여전히 연결 리스트로 표현된 입력도 처리할 수 있어야 함
= 연결리스트를 더는 사용하지 않아도 관련 코드를 지울 수 없음
너무 많은 공간을 차지할 수 있다.
Entry와 연결 정보는 사실 내부 구현에 해당하므로 직렬화에 포함할 가치가 없다.
이는 저장, 통신 속도 저하로 이어진다.
시간이 너무 많이 걸릴 수 있다.
객체 그래프의 위상에 대한 정보가 없어 직접 그래프를 순회해볼 수 ㅏㅂㄲ에 없다.
스택 오버플로우를 일으킬 수 있다.
StringList는 무자열을 나열하는 정도의 직렬화면 충분할 것 같다.
= 물리적인 상세 표현은 배제한 채 논리적인 구성만 담는 것이다.
transient : 일시적이다. 해당 인스턴스 필드가 기본 직렬화 형태에 포함되지 않는다.
transient여도 writeObject와 readObject로 defaultWriteObject, defaultReadObject를 호출하는 작업은 꼭 필요하다. → 향후 transient가 아닌 인스턴스 필드가 추가되더라도 호환된다.
ex) 신버전 인스턴스를 직렬화 한 후 구버전으로 역직렬화하면 새로 추가된 필드들은 무시될 것이다.
하지만 defaultReadObject가 호출되지 않으면 역직렬화에서 StreamCorruptedException이 발생할 것이다.
이전보다 개선 버전의 StringList는 문자열의 길이가 평균 10일 때 공간 절반, 속도 두 배가 된다.
또한 스택 오버플로가 발생하지 않는다.
StringList보다 더 심한 클래스도 있다.
유연성과 성능이 떨어졌을 뿐이지 객체를 직렬화 한 후 역직렬화 하면 원래 객체르 륵 불변식가지 포함해 제대로 복원해내긴 한다. → 정확한 객체
하지만 그 불변식이 세부 구현에 따라 달라지는 객체가 있다.
ex) 해시테이블
물리적으로는 키-값 엔트리를 나열한 형태
논리적으로는 어떤 엔트리를 어떤 버킷에 담을지 키에서 구한 해시코드가 결정함. 그 계산 방식은 구현에 따라, 계산할 때마다 달라지기도 함
→ 해시테이블에 기본 직렬화 = 심각한 버그
기본 직렬화를 수용하든 하지않든 defaultWriteObject 메서드를 호출하면 transient로 선언하지 않은 모든 인스턴스 필드가 직렬화된다.
따라서 transient로 선언해도 되는 인스턴스 필드에서는 모두 transient 한정자를 붙여야 한다.
ex) 네이치브 자료구조를 가리키는 long 필드
등이 이에 해당한다.
해당 객체의 논리적 상태와 무관한 필드라고 확신할 대만 transient 한정자를 생략하자.
→ 커스텀 직렬화에서는 대부분의 인스턴스 필드를 transient로 선언해야 한다.
기본 직렬화를 사용할 때 transient 필드들은 역직렬화될 때 기본값(null, false, 0)으로 초기화 된다.
기본값을 그대로 사용해서는 안된다면 defaultReadObject에서 원하는 값으로 복원하거나, 처음 사용할 때 초기화 하자(Item 83)
기본 직렬화 사용 여부와 관계 없이 객체의 전체 상태를 읽는 메서드에 적용해야 하는 동기화 매커니즘을 직렬화에도 사용하라.
모든 메서드를 syncronized로 선언하여 스레드 안전하게 만든 객체(Item 82) 에서 기본 직렬화를 사용하려면 writeObject도 syncronized 선언하라.
writeObject 메서드 안에서 동기화하고 싶다면 클래스의 다른 부분에서 사용하는 락 순서를 똑같이 따라야 한다. → 자원순서교착상태
어떤 직렬화 형태를 택하든 직렬화 가능 클래스 모두에 직렬버전 UID를 명시적으로 부여하자.
고유할 필요 없다.
기존 버전 클래스와 호환성을 끊고 싶다면 단순히 직렬 버전 UID의 값을 바꿔줘라
하지만 이런 이유가 아니면 절대 수정하지 마라. → InvaildClassException
The text was updated successfully, but these errors were encountered: