본문

160107P(목)

Java -  Chapter17 객체의 직렬화

 

자바는 모든 입출력 데이터를 스트림 형태로 주고받는다.

따라서 객체를 파일에 저장하거나 네트워크로 전송하기 위해서는 객체를 스트림으로 만드는것이 필요하다.

 

직렬화(serialization)

java.io.ObjectOutputStream

객체를 스트림으로 만드는 작업

 

class ObjectOutputExample1 {

public static void main(String args[]) {

ObjectOutputStream out = null;

 

try {

out = new ObjectOutputStream(new FileOutputStream("output.dat"));

 

out.writeObject(new GregoranCalendar(2006, 0, 14));

out.writeObject(new GregoranCalendar(2006, 0, 15));

out.writeObject(new GregoranCalendar(2006, 0, 16));

}

catch(IOException ioe) {

System.out.println("파일로 출력할 수 없습니다.");

}

finally {

try {

out.close();

}

catch(Exception e) {

}

}

}

}

 

역직렬화(deserialization)

java.io.ObjectInputStream

프로그램에 입력된 스트림으로부터 다시 객체를 만들어내는 작업

 

class ObjectInputExample1 {

public static void main(String args[]) {

ObjectInputStream in = null;

 

try {

in = new ObjectInputStream(new FileInputStream("output.dat"));

 

while(true) {

GregorianCalender calender = (GregorianCalender) in.readObject();

 

int year = calender.get(Calender.YEAR);

int month = calender.get(Calender.MONTH) + 1;

int date = calender.get(Calender.DATE);

 

System.out.println(year + "/" + month + "/" + date);

}

}

catch(FileNotFoundException fnfe) {

System.out.println("파일이 존재하지 않습니다.");

}

catch(EOFException eofe) {

System.out.println("끝");

}

catch(IOException ioe) {

System.out.println("파일을 읽어올 수 없습니다.");

}

catch(ClassNotFoundException cnfe) {

System.out.println("해당 클래스가 존재하지 않습니다.");

}

finally {

try {

in.close();

}

catch(Exception e) {

}

}

}

}

 

직렬화 가능 클래스

java.io.Serializable 인터페이스를 구현하는 클래스

이외는 전부 직렬화 불가 클래스들이다.

 

객체를 직렬화할 때 스트림으로 만들어지는 구성요소는 필드뿐이다.

생성자나 메소드도 스트림으로 만들어지는것은 아니다.

static 필드도 직렬화 대상이 아니다.

 

transient 키워드

해당 필드는 직렬화에서 제외시킨다.

ex)

transient String passwd;

 

primitive 타입과 직렬화 가능 클래스 타입의 필드는 모두 직렬화 가능

그 밖의 타입은 모두 직렬화 불가능

필드의 타입체크는 필드의 선언된 타입이 아니라, 필드 값의 타입에 대해 이루어 진다.

ex)

obj.addAttachment(new Object); (X)

obj.addAttachment("모카자바 500g 15500원"); (O)

하지만 필드값에 따라서 실행여부가 결정되는것은 좋은 프로그램이 아니다.

 

ObjectOutputStream의 writeObject는 하나라도 직렬화 대상 필드가 아닌것이 있다면 IOException을 발생시킨다.

 

디폴트 직렬화 메커니즘

java.io.Serializable 인터페이스를 implements하고 static이나 transient를 제외한 필드들을 직렬화 하는 것

 

커스텀 직렬화, 역직렬화 메소드

디폴트 직렬화 메커니즘은 희소배열(sparse array)등 을 직렬화할 때 메모리와 전송시간을 낭비한다.

 

class DistrChart implements Serializable {

int arr[][];

...

private void writeObject(ObjectOutputStream out) throws IOException {

for(int row = 0; row < arr.length; row++) {

for(int col = 0; col < arr[0].length; col++) {

if(arr[row][col] != 0) {

out.writeInt(row);

out.writeInt(col);

out.writeInt(arr[row][col]);

}

}

}

}

}

 

writeInt

writeChar

writeDouble

writeObject

...

 

※ 직렬화 메소드 안에서 write- 메소드를 호출한 순서와 일치해야 한다.

class DistrChart implement Serializable {

int arr[][];

...

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {

// 역직렬화할 때는 직렬화 가능 클래스의 생성자가 호출되지 않으므로 배열을 선언해줘야 한다.

arr = new int[10][10];

 

try {

while(true) {

int row = in.readInt();

int col = in.readInt();

int data = in.readInt();

arr[row][col] = data;

}

catch(EOFException e) {

}

}

}

 

ObjectOutputStream.writeObject를 호출하면 직렬화 가능 클래스의 writeObject 메소드가 간접적으로 호출된다.

ObjectInputStream.readObject를 호출하면 직렬화 가능 클래스의 readObject 메소드가 간접적으로 호출된다.

throws해서 에러처리하는 이유는 원래의 writeObject와 readObject로 에러를 던져주기 위해서다.

 

다른 클래스를 상속받는 직렬화 가능 클래스

직렬화는 제대로 동작하지만 역직렬화에서 IOException : no valid constructor 에러가 난다.

∵ 직렬화 가능 클래스 자체의 생성자는 호출이 안되지만, 직렬화가 불가능한 가장 가까운 슈퍼클래스의 no-arg constructor가 자동으로 호출된다. 따라서 역직렬화할 때 기본 생성자를 생성해 주어야 한다.

 

몇몇 필드의 값이 제대로 출력되지 않는다.

직렬화 가능 클래스의 필드는 직렬화 되지만 그의 슈퍼클래스 등의 필드는 직렬화 되지 않는다.

∵ 직렬화 가능한 클래스 안에 직렬화 및 역직렬화 메소드를 구현해줘야 한다.

 

class BookInfo extends GoodsInfo implements Serializable {

String writer;

int page;

 

BookInfo(String name, String code, int price, String writer, int page) {

super(name, code, price);

this.writer = writer;

this.page = page;

}

 

private void writeObject(ObjectOutputStream out) throws IOException {

out.writeUTF(code);

out.writeUTF(name);

out.writeUTF(price);

out.writeUTF(writer);

out.writeUTF(page);

}

 

private void readObject(ObjectInputSteam in) throws IOException, ClassNotFoundException {

code = in.readUTF();

name = in.readUTF();

price = in.readUTF();

writer = in.readUTF();

page = in.readUTF();

}

}

 

ObjectOutputStream.defaultWriteObject

직렬화 메소드 안에 클래스 자신의 모든 필드를 직렬화하는 명령문이 포함 되어있을 때 대체가능

 

class BookInfo extends GoodsInfo implements Serializable {

String writer;

int page;

 

BookInfo(String name, String code, int price, String writer, int page) {

super(name, code, price);

this.writer = writer;

this.page = page;

}

 

private void writeObject(ObjectOutputStream out) throws IOException {

out.writeUTF(code);

out.writeUTF(name);

out.writeUTF(price);

out.defaultWriteObject();

}

 

private void readObject(ObjectInputSteam in) throws IOException, ClassNotFoundException {

code = in.readUTF();

name = in.readUTF();

price = in.readUTF();

in.defaultReadObject();

}

}

 

직렬화 가능 클래스의 버전관리

직렬화 가능 클래스를 수정해버리면 클래스 버전이 달라져 버리기 때문에 에러 발생

 

버전 번호는 자바의 직렬화 메커니즘과 역직렬화 메커니즘이 실행될 때 자동으로 부여

따라서 구성요소가 바뀌면 다른 번호가 부여

 

static final long seralVersionUID = 100;

이렇게 붙여진 버전은 구성요소를 추가하거나 삭제해도 바뀌지 않는다.

 

serialver 클래스_이름

중복문제를 피하기위해서 프롬프트 창에서 serialver Rectangle 이라고 입력하면 serialVersionUID가 생성된다.

그다음 static final long seralVersionUID = 부여받은_Version넘버; 라고 코드에 써넣으면 된다.

'Programming > Java' 카테고리의 다른 글

160117P(일)  (0) 2016.01.17
160109P(토)  (0) 2016.01.10
160108P(금)  (0) 2016.01.09
160103P(일)  (0) 2016.01.04
151220P(일)  (0) 2015.12.20

공유

댓글