packagerule75;/**
* 사용자가 지정 직렬화 형식을 사용하면 좋을지 따져 보라
*
* 어떤 직렬화 형식이 적절할지 따져보지도 않고 기본 직렬화 형식(Default Serialized Form)을 그대로 받아들이지 마라.
* 기본 직렬화 형식은 그 객체의 물리적 표현이 논리적 내용과 동일할 때만 적절하다.
* 즉, 어떤 객체의 가장 효과적인 직렬화 방식은 해당 객체가 나타내는 논리적 데이터만 담아야하며, 물리적 표현과는 무관해야 한다.
*
* @author gwon
* @history
* 2019. 6. 2. initial creation
*/publicclassRule75{}
packagerule75;importjava.io.Serializable;/**
* 논리적으로 사람의 이름을 표현할 때는 문자열로 구성되므로 문자열로 사람의 이름을 표현하는
* 이 클래스는 기본 직렬화 형식을 그대로 써도 좋다.
*
* '@serial' 태그는 Javadoc에서 제공한다.
* private 필드여도 Serializable을 구현한 클래스면 접근 가능하므로 Javadoc에 표현해야 한다.
*
* @author gwon
* @history
* 2019. 6. 2. initial creation
*/publicclassNameClassGoodUseDefaultSerializedFormimplementsSerializable{privatestaticfinallongserialVersionUID=1L;/**
* 성(last name). null이 될 수 없다.
*
* @serial
*/privateStringlastName;/**
* 이름(first name). null이 될 수 없다.
*
* @serial
*/privateStringfirstName;/**
* 중간 이름(middle name). null이 될 수 있다.
*
* @serial
*/privateStringmiddleName;}
packagerule75.compare;importjava.io.ByteArrayOutputStream;importjava.io.IOException;importjava.io.ObjectOutputStream;importjava.io.Serializable;/**
* 링크드리스트로 문자열의 리스트를 표현하는 클래스이다.
* 이 클래스를 기본 직렬화 형식으로 직렬화 할 경우 모든 연결 리스트 항목과 항목 간 양방향 연결 구조를 모두 직렬화해야 한다. 따라서
* 1. 너무 많은 공간을 필요로 할 수 있다. (네트워크 속도, 디스크 저장 문제 발생가능)
* 2. 너무 많은 시간을 소비하는 문제가 생길 수 있다.
* 3. 스택 오버플로가 발생할 수 있다.
*
*
*
* 하지만, 이 클래스를 논리적으로만 생각한다면 문자열을 순서대로 저장한 배열이다. 따라서 기본 직렬화 형식이 아닌 사용자 지정 직렬화를 구현해서 직렬화하는 것이 좋다.
*
* @author gwon
* @history
* 2019. 6. 2. initial creation
*/publicclassStringListClassNotGoodUseDefaultSerializedFormimplementsSerializable{privatestaticfinallongserialVersionUID=1L;privateintsize=0;privateEntryhead=null;privatestaticclassEntryimplementsSerializable{Stringdata;Entrynext;Entryprevious;}publicvoidadd(Stringdata){size++;if(head==null){head=newEntry();head.data=data;}else{EntrynewEntry=newEntry();newEntry.data=data;head.next=newEntry;newEntry.previous=head;head=newEntry;}}/**
* 기본 직렬화 형식을 사용해서 테스트해본 결과, 3482번째에서 스택 오버플로 발생
*/publicstaticvoidmain(String[]args)throwsIOException{StringListClassNotGoodUseDefaultSerializedFormstringList=newStringListClassNotGoodUseDefaultSerializedForm();for(inti=0;i<5000;i++){System.out.println(i);stringList.add(String.valueOf(i));byte[]serializedMember;try(ByteArrayOutputStreambaos=newByteArrayOutputStream()){try(ObjectOutputStreamoos=newObjectOutputStream(baos)){oos.writeObject(stringList);serializedMember=baos.toByteArray();}}}}}
packagerule75.compare;importjava.io.ByteArrayOutputStream;importjava.io.IOException;importjava.io.ObjectInputStream;importjava.io.ObjectOutputStream;importjava.io.Serializable;/**
* 문자열 리스트는 그저 리스트에 담기는 문자열의 순서이다.
* 따라서 논리적으로 필요한 데이터만 직렬화하고, 역직렬화한다.
*
* '@serialData' 태그는 Javadoc에서 제공한다.
* private 메서드여도 Serializable을 구현한 클래스면 접근 가능하므로 Javadoc에 표현해야 한다.
*
* @author gwon
* @history
* 2019. 6. 2. initial creation
*/publicclassStringListClassGoodUseUserSerializedFormimplementsSerializable{privatestaticfinallongserialVersionUID=1L;// transient를 사용하여 필요없는 값은 직렬화에서 제외privatetransientintsize=0;privatetransientEntryhead=null;// Serializable 구현하지 않음privatestaticclassEntry{Stringdata;Entrynext;Entryprevious;}publicvoidadd(Stringdata){size++;if(head==null){head=newEntry();head.data=data;}else{EntrynewEntry=newEntry();newEntry.data=data;head.next=newEntry;newEntry.previous=head;head=newEntry;}}/**
* 사용자 지정 직렬화
*
* @serialData 리스트의 크기가 먼저 기록되고, 그 다음에는 모든 문자열이 순서대로 기록된다.
* @param s
* @throws IOException
*/privatevoidwriteObject(ObjectOutputStreams)throwsIOException{s.defaultWriteObject();// 객체의 모든 필드가 transient일 때도 호출하는게 좋다.s.writeInt(size);for(Entrye=head;e!=null;e=e.next){s.writeObject(e.data);}}/**
* 사용자 지정 역직렬화
*
*
* @param s
* @throws IOException
* @throws ClassNotFoundException
*/privatevoidreadObject(ObjectInputStreams)throwsIOException,ClassNotFoundException{s.defaultReadObject();// 객체의 모든 필드가 transient일 때도 호출하는게 좋다.intnumElements=s.readInt();for(inti=0;i<numElements;i++){add((String)s.readObject());}}/**
* 사용자 지정 직렬화 형식을 사용해서 테스트해본 결과, 스택 오버플로 발생하지 않음
*/publicstaticvoidmain(String[]args)throwsIOException{StringListClassGoodUseUserSerializedFormstringList=newStringListClassGoodUseUserSerializedForm();for(inti=0;i<1000000;i++){System.out.println(i);stringList.add(String.valueOf(i));byte[]serializedMember;try(ByteArrayOutputStreambaos=newByteArrayOutputStream()){try(ObjectOutputStreamoos=newObjectOutputStream(baos)){oos.writeObject(stringList);serializedMember=baos.toByteArray();}}}}}
packagerule75;importjava.io.IOException;importjava.io.ObjectOutputStream;importjava.io.Serializable;/**
* 객체를 직렬화 할 대는 객체의 상태 전부를 읽는 메서드에 적용할 동기화 수단을 반드시 적용해야 한다.
*
* @author gwon
* @history
* 2019. 6. 2. initial creation
*/publicclassSynchronizedWriteObjectClassimplementsSerializable{privatestaticfinallongserialVersionUID=1L;/**
* 모든 메서드를 동기화해서 스레드 안전성을 달성하는 객체가 있다면, 직렬화에도 동기화 사용
*
* @param s
* @throws IOException
*/privatesynchronizedvoidwriteObject(ObjectOutputStreams)throwsIOException{s.defaultWriteObject();// 객체의 모든 필드가 transient일 때도 호출하는게 좋다.}}