'객체 직렬화란?'에 해당되는 글 1건

  1. 2013.12.23 [펌] 객체 직렬화란?
01.JAVA/Java2013. 12. 23. 11:43
반응형

객체 직렬화란?

- 직렬화: Heap에 위치한 객체를 출력 가능한 상태로 만드는 작업

- 역직렬화: 직렬화된 객체를 다시 Heap에 넣기 위한 작업

- 직렬화의 대상은 객체의 Attribute의 값. (메소드는 그저 주소값만 필요할뿐)

 

- 객체를 IO하기 위해서는 필터스트림인 ObjectInputStream과 ObjectOutputStream이 필요

- 직렬화는 ObjectOutputStream

- 역직렬화는 ObjectInputStream

- 직렬화 대상객체는 java.io.Serializable를 implements한 클래스 객체여야 한다.

 



 

- 자바의 메모리 구조는 그림과 깉이 되있는데 여기서 보듯이 객체는 Attribute 데이터, 그리고 Method의 주소만 갖고 있을 뿐이다.

따라서 직렬화의 대상은 실체가 없는 Method는 제외하고 Attribute만 직렬화하게 된다.




객체 직렬화 하는 방법

- Serializable Interface를 implements한 class의 객체는 뭐든 직렬화가 가능하다.



ex) 직렬화 대상 클래스

 

import java.io.*; 

public class Member implements Serializable{
    private String name; 
    private transient int age;
    private String address; 
    private transient MyDate birthday;
    ...
}


ex) 객체 직렬화 하기

 

public void writeMemberObject(Member m) throws IOException {
    ObjectOutputStream oos = null; 
    try{ 
        oos = new ObjectOutputStream(new FileOutputStream("mem.obj")); // 연결 + 필터 추가 
        oos.writeObject(m); // 객체를 출력하는 메소드: writeObject(Object obj) 
        // write할 때 Exception이 발생하는데 이 때 close를 안하면 스트림이 안닫혀있는 상태로 방치 
    } finally { 
        if(oos!=null) { 
            oos.close(); // 그래서 close로 닫아줌 
        } 
    } 
}





객체 직렬화 피하기 (또는 피해야 되는 경우)

 

- 객체 Instance 변수 앞에 transient 키워드를 붙이면 직렬화 대상에서 제외된다.

- 그런데 왜 직렬화 대상에서 제외할까? 답은...


1. 보안적인 측면에서 직렬화 할 때 빼고 싶을 때

 보안 문제로 직렬화 하면 안되는 어트리뷰트가 여기에 해당한다.


2. 직렬화 대상이 아닌 객체 type일 경우

 만약 다음과 그림과 같은 A 객체와 B 객체를 직렬화 한다고 해보자.


위와 같은 구조에서 A, B 객체들을 직렬화하고 싶을 때 어떻게 할까?


답은 B 객체의 멤버 인스턴스인 C 객체에 transient 키워드를 붙여준다.

그러면 직렬화 대상에서 제외되므로 소스 코드를 통채로 고치지 않아도 된다.






역직렬화 객체를 복원하는 방법

- 객체를 만든 뒤 readObject() 메소드의 리턴하는 Attribute 대입

- 자바가 역직렬화하는 방식은 JVM이 역직렬화 하고자 하는 객체를 만들어서 리턴한다.



ex)

 

public Member readMemberObject() throws IOException, ClassNotFoundException {
    ObjectInputStream ois = null; 
    Member m = null; 
    try    { 
        ois = new ObjectInputStream(new FileInputStream("mem.obj")); 
        m = (Member)ois.readObject(); 
    } finally { 
        ois.close(); 
        return m; 
    } 
}

 



그런데 여기서 문제가 발생한다.

만약 역직렬화해서 만든 객체(readObject() 메소드로 불러들인 객체)의 클래스가 변경이 되었다면?

그런데도 역직렬화한 객체를 변경된 클래스의 인스턴스에 대입하려고 한다면?


당연히 이 때 JVM은 역직렬화 객체를 대입할 수 없다고 예외를 발생시킨다.

좀 더 자세히 살펴보면 역직렬화한 객체와 새로 변경된 클래스의 serialVersionUID가 일치하지 않아서 발생하는 문제인데

새로 변경된 클래스와 역직렬화하는 객체의 serialVerionUID를 일치시켜주면 된다.


그럼 또 어떻게 serialVerionUID를 일치시켜준단 말인가?

답은...


클래스를 작성할 때 미리 serialVerionUID를 상수로 고정시켜 놓는다. 이렇게 되면 클래스가 새로 변경이 되도 역직렬화하는 객체와 serialVerionUID가 같기 때문에 예외가 더 이상 발생하지 않는다.



ex) 직렬화 대상 클래스 (역직렬화 문제 해결)

 

import java.io.*; 

public class Member implements Serializable{
    // 직렬화, 역직렬화 시 serialVersionUID 상수가 없으면 JVM이 만들어서 넣어준다. 
    // 있으면 만들지 않는다. 
    static final long serialVersionUID = 100L;
    private String name; 
    private transient int age;
    ...
}
Posted by 1010