티스토리 뷰

반응형

Effective Java 에서 익숙하게 발견하는 단어가 있습니다.


직렬화(Serializable)


이 직렬화는 과연 무엇일까요?


직렬화


자바에서 객체 안에 저장되어 있는 내용을 파일로 저장하거나 네트워크를 통하여 다른 곳으로 전송하려면 객체를 바이트 형태로 일일이 분해해야 합니다. 이러한 분해하는 과정. 즉, 바이트 단위로 바꿔주는 형태를 직렬화 라고 합니다. 자바는 Serializable 인터페이스를 구현한 클래스만 직렬화할 수 있도록 제한하고 있습니다. private 필드를 포함한 모든 필드를 바이트로 변환해도 좋다는 표시 역활을 합니다.


public class XXX implements Serializable{ }


무엇이든 예외가 있듯이 직렬화에도 예외가 있습니다. transient 가 붙은 경우에는 직렬화가 되지 않습니다.

public class XXX implements Serializable{
    public int field1;
    protected int field2;
    private transient int filld3; //transient 키워드가 붙은 필드는 직려화에서 제외 된다.
}


serialVersionUID

직렬화된 객체를 역직렬화 할 때 직렬화했을 때와 같은 클래스를 사용해야합니다.


만약 serialVersionUID가 다를 경우 예외가 발생하게 됩니다.


이 serialVersionUID는 Serializable 인터페이스를 구현한 클래스를 컴파일하면 자동적으로 serialVersionUID 값이달라집니다. 


만약 컴파일 후 불가피하게 클래스의 수정이 필요할 경우 serialVersionUID를 명시적으로 선언하면 됩니다.

 public class XXX implements Serializable{
    static final long serialVersionUID = 정수값;
}

<JDK 설치 경로> \bin 폴더에 serialVersionUID 값을 자동 생성시켜주는 serialver.exe 명령어를 제공해준다.



writeObjcet()와 readObject() 메소드

두 클래스가 상속 관계가 있다고 가정 시, 


부모 클래스가 Serializable 인터페이스를 구현하고 있으면  자식 클래스는 Serializable을 구현하지 않아도 자식 객체를 직렬화하면 부모 자식 모두 직렬화 됩니다.


반대인 경우 (자식만 Serializable 구현한 경우)는 자식 객체를 직렬화 할 때 부모는 제외됩니다.


이 경우 부모도 하고 싶으면 다음과 같은 방식으로 구현합니다.


1. 부모 클래스가 Serializable 인터페이스를 구현하도록 합니다.

2. 자식 클래스에서 writeObject()와 readObject() 메소드를 선언해서 부모 객체의 필드를 직접 출력합니다.


두 번째 방식에서 접근 제한자가 private가 아니면 자동 호출되지 않기 때문에 꼭 private로 붙여 주어야 합니다.


public class TEST {
    public static void main(String[] args) throws Exception{
        try {
            FileOutputStream fos = new FileOutputStream("C:\\Users\\emcast_shin\\Desktop\\Object1.dat");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            Child child = new Child();
            child.field1 = "A";
            child.field2 = "B";
            child.field3 = "C";
            oos.writeObject(child);
            oos.flush();
            oos.close();
            fos.close();
 
            FileInputStream fis = new FileInputStream("C:\\Users\\emcast_shin\\Desktop\\Object1.dat");
            ObjectInputStream ois = new ObjectInputStream(fis);
            Child v = (Child) ois.readObject();
            System.out.println("filed1: " + v.field1);
            System.out.println("filed2: " + v.field2);
            System.out.println("filed3: " + v.field3);
            ois.close(); fis.close();
        }catch (FileNotFoundException e){
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}
 
/**
 * 부모 클래스 Serializable을 안하였다.
 */
class Parent{
    public String field1;
}
 
/**
 * 자식 클래스 Serializable을 하였고, 부모 클래스는 Serializable을 안하였다.
 */
class Child extends Parent implements Serializable {
    public String field2;
    public transient String field3;  //Serializable을 제외하기 위해서 넣었다.
 
    private void writeObject(ObjectOutputStream out) throws IOException {
        out.writeUTF(field1); //부모 객체의 필드값 출력함
        out.defaultWriteObject();  //자식 객체의 필드값을 직렬화
    }
 
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{
        field1 = in.readUTF(); // 부모 객체의 필드값을 읽어옴
        in.defaultReadObject(); // 자식 객체의 필드값을 역직렬화
    }
}


참고 : 이것이 자바다



반응형
댓글