본문으로 바로가기
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

자바 객체의 직렬화(Serializable)

자바에서 입출력에 사용되는 것은 스트림이라는 데이터 통로를 통해 이동했습니다. 하지만 객체는 바이트형이 아니라서 스트림을 통해 파일에 저장하거나 네트워크로 전송할 수 없습니다. 따라서 객체를 스트림을 통해 입출력하려면 바이트 배열로 변환하는 것이 필요한데, 이를 '직렬화' 라고 합니다. 반대로 스트림을 통해 받은 직렬화된 객체를 원래 모양으로 만드는 과정을 역직렬화라고 합니다.



http://blog.naver.com/PostView.nhn?blogId=kkson50&logNo=220564273220&parentCategoryNo=&categoryNo=33&viewDate=&isShowPopularPosts=false&from=postView


직렬화 조건

1) 기본형 타입(int, char, String, short, double, long, byte 등등)은 직렬화가 가능하다.

2) Serializable 인터페이스를 구현한 객체여야 한다.

3) transient가 사용된 멤버는 전송되지 않는다.

4) 생성자나 메소드는 직렬화 대상에 속하지 않는다.


serialVersionUID를 선언해야 하는 이유

자바가상기계 (JVM)은 직렬화와 역직렬화를 하는 시점의 클래스에 대한 버전 번호를 부여합니다. 만약 그 시점에 클래스의 정의가 바뀌어 있다면 새로운 버전 번호를 할당합니다. 그래서 직렬화할 때의 버전 번호와 역직렬화를 할 때의 버전 번호가 다르면 역직렬화가 불가능하게 될 수도 있습니다. 이런 문제를 해결하기 위해 SerialVerionUID를 사용합니다.

간단명료하게 serialVersionUID값을 저장할 때 클래스 버전이 맞는지 확인하기 위한 용도입니다.

만약 직렬화할 때 사용한 serialVersionUID의 값과 역직렬화 하기 위해 사용했던 serialVersionUID값이 다르다면 InvalidClassException이 발생할 수 있습니다.


1. 직렬화 클래스 만들기

객체를 직렬화/역직렬화 하기 위해서는 먼저 객체 자체가 직렬화가 가능한 클래스로부터 생성된 객체여야 합니다. 직렬화가 가능한 클래스를 만들기 위해서는 java.io.Serializable 인터페이스를 받아서 만들 수 있습니다.



import java.io.Serializable; public class Person implements Serializable{ private static final long serialVersionUID = -4865946674835353945L; private String name;    //직렬화 대상 private int age;     //직렬화 대상 Person(String name, int age) { //생성자는 직렬화 대상이 아니다. this.name = name; this.age = age; } public void personInformation() { //메서드는 직렬화 대상이 아니다. System.out.println("name : " + name + " , age : " + age); } }


2. 객체를 직렬화 하기

객체를 직렬화/역직렬화 하기 위해서는 java.io.ObjectOutputStream과 java.io.ObjectInputStream이라는 패키지를 사용합니다. 


객체를 직렬화하는 과정

1) 객체를 직렬화해 쓰는 클래스인 ObjectOutputStream 객체를 생성하고 파라미터에 출력 스트림 객체를 넣어 직렬화하는 스트림을 만든다.

2) 스트림을 통해 출력할 객체를 생성한다.

3) writeObject 메서드를 사용하여 객체를 직렬화한 뒤 스트림으로 흘려 보낸다.

4) close 메서드를 사용하여 파일을 닫는다.


import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.Serializable; class Person implements Serializable{ private static final long serialVersionUID = -4865946674835353945L; private String name; //직렬화 대상 private int age; //직렬화 대상 Person(String name, int age) { //생성자는 직렬화 대상이 아니다. this.name = name; this.age = age; } public void personInformation() { //메서드는 직렬화 대상이 아니다. System.out.println("name : " + name + " , age : " + age); } } public class Test { public static void main(String[] args) { ObjectOutputStream out = null; try { out = new ObjectOutputStream(new FileOutputStream("ktko.dat")); Person p1 = new Person("ktko", 29); Person p2 = new Person("kwko", 31); out.writeObject(p1); out.writeObject(p2); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } }


실행 결과에는 아무것도 나오지 않지만 프로젝트를 refresh하면 "ktko.dat"라는 파일이 생성되었고, 생성된 파일을 열어보면 사람이 읽을 수 없느 바이트 형식의 객체로 표현되어 잇음을 알 수 있습니다.


3. 객체를 역직렬화 하기

바이트로 표현된 객체를 다시 원래대로 읽어 오기 위해서는 객체를 역직렬화하는 과정이 필요합니다. 


객체를 역직렬화하는 과정

1. 입력을 위해 InputStream 클래스를 상속받은 클래스의 객체를 생성한다.

2. 객체를 역직렬화하는 클래스인 ObjectInputStream의 객체를 생성하고 파라미터에 입력 스트림 객체를 넣어 역직렬화하는 스트림을 만든다.

3. readObject 메소드를 사용하여 스트림에서 객체를 역직렬화해 읽어오고 값을 미리 선언한 알맞은 객체 변수에 저장한다.


class Person implements Serializable{ private static final long serialVersionUID = -4865946674835353945L; private String name; //직렬화 대상 private int age; //직렬화 대상 Person(String name, int age) { //생성자는 직렬화 대상이 아니다. this.name = name; this.age = age; } public void personInformation() { //메서드는 직렬화 대상이 아니다. System.out.println("name : " + name + " , age : " + age); } } public class Test { public static void main(String[] args) { ObjectInputStream in = null; try { in = new ObjectInputStream(new FileInputStream("ktko.dat")); Person getP1 = (Person) in.readObject(); getP1.personInformation(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } }


실행 결과


ObjectInputStream 클래스를 이용하여 파일에서 객체를 역직렬화해 읽어 올 때는 더는 읽을 데이터가 없으면 EOFException이 발생하게 됩니다. 따라서 try~catch 구문을 이용하여 EOFException을 잡는 catch 블록을 만들고 파일을 끝까지 읽었을 때 어떤 처리를할 것인지 기술해 줍니다.



class Person implements Serializable{
    private static final long serialVersionUID = -4865946674835353945L;
    private String name;    //직렬화 대상
    private int age;        //직렬화 대상
    
    Person(String name, int age) { //생성자는 직렬화 대상이 아니다.
        this.name = name;
        this.age = age;
    }
    
    public void personInformation() { //메서드는 직렬화 대상이 아니다.
        System.out.println("name : " + name + " , age : " + age);
    }
}

public class Test {
    public static void main(String[] args) {
        ObjectOutputStream out = null;
        try {
            out = new ObjectOutputStream(new FileOutputStream("ktko.dat"));
            Person p1 = new Person("ktko1", 29);
            out.writeObject(p1);
            Person p2 = new Person("ktko2", 29);
            out.writeObject(p2);
            Person p3 = new Person("ktko3", 29);
            out.writeObject(p3);
            
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        
        ObjectInputStream in = null;
        try {
            in = new ObjectInputStream(new FileInputStream("ktko.dat"));
            Person getP;
            
            while((getP=(Person)in.readObject()) != null)
                getP.personInformation();
        } catch (EOFException e) {
            e.printStackTrace();
            System.out.println("읽을 데이터가 없습니다");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}