직렬화(Serialization)와 역직렬화(Deserialization)

이진호·2022년 9월 2일
0

JAVA

목록 보기
6/10
post-thumbnail

직렬화(Serialization)는 객체의 상태를 바이트 스트림으로 변환하는 메커니즘입니다. 반대로 역직렬화(Deserialization)는 바이트 스트림을 실제 자바 객체로 재생성 재생성하는 메커니즘입니다. 이런 메커니즘들은 객체를 저장하거나 전송하는데 사용됩니다.

생성된 바이트 스트림은 플랫폼에 독립적입니다. 따라서 한 플랫폼에서 직렬화된 객체는 다른 플랫폼에서 역직렬화되어 사용가능합니다.

직렬화의 장점

  1. 객체의 상태를 저장하는데 용이합니다.
  2. 네트워크로 객체를 전송하는데 용이합니다.

Serializable 인터페이스

Java 객체를 직렬화하기 위해서는 아래와 같이 java.io.Serializable 인터페이스를 구현해야 합니다.

public class JavaClass implements Serializable {
   ...
}

Serializable은 마커 인터페이스(데이터와 메소드를 가지지 않는 인터페이스)입니다. 마커 인터페이스는 해당 객체가 특정 기능을 수행할 수 있도록 표시하는데 사용됩니다. 마커 인터페이스의 다른 예로는 CloneableRemote 가 있습니다.

ObjectOutputStream 클래스와 ObjectInputStream 클래스

ObjectOutputStream 클래스와 ObejctInputStream 클래스는 각각 객체 직렬화, 역직렬화 메소드를 제공하는 스트림 클래스입니다.

ObjectOutputStream 클래스는 객체를 직렬화시키는 writeObject() 메소드를 가지고 있습니다.

public final void writeObject(Object obj) throws IOException

ObjectInputStream 클래스는 객체를 역직렬화시키는 readObject() 메소드를 가지고 있습니다.

public final Object readObject() throws IOException, ClassNotFoundException

writeObject() 메소드 내부에는 아래와 같이 Serializable 인터페이스 구현체 여부를 확인 하는 로직이 존재합니다. Serializable 인터페이스를 구현하지 않은 객체(Enum, String 제외)가 들어올 경우 NotSerializableException 예외가 발생하게 됩니다.

if (obj instanceof String) {
    writeString((String) obj, unshared);
} else if (cl.isArray()) {
    writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
    writeEnum((Enum<?>) obj, desc, unshared);
} else if (obj instanceof Serializable) {
    writeOrdinaryObject(obj, desc, unshared);
} else {
    if (extendedDebugInfo) {
        throw new NotSerializableException(
            cl.getName() + "\n" + debugInfoStack.toString());
} else {
        throw new NotSerializableException(cl.getName());
    }
}

기억할 점

  1. 부모 클래스가 Serializable이 구현되어있을 경우 자식 클래스는 Serializable를 구현할 필요가 없습니다.
  2. non-static한 멤버 변수들만 직렬화 프로세스를 통해 저장됩니다.
  3. 정적 멤버 변수들과 transient 키워드 변수들을 직렬화 프로세스를 통해 저장되지 않습니다. non-static한 멤버 변수들중 직렬화에서 제외시키고 싶다면 아래와 같이 transient 키워드를 추가하면 됩니다.
public class File implements Serializable {
    ...
    private transient PathStatus status = null;
    ...
}
  1. 역직렬화 과정에서 생성자는 호출되지 않습니다.
  2. 연관객체들도 반드시 Serializable 인터페이스를 구현해야합니다.
class A implements Serializable{

    // B also implements Serializable interface.
    B ob=new B();  
}

serialVersionUID

  • Serializable을 구현하는 Class의 경우 Class의 versioning 용도로 serialVersionUID 변수를 사용합니다.
  • 이 변수는 역직렬화 과정 중 발신자와 수신자가 로드한 클래스가 동일한 클래스인지 확인하는데 사용됩니다.
  • 수신자가 해당 발신자의 클래스와 다른 UID를 가진 객체에 대한 클래스를 로드한 경우 Deserialization은 InvalidClassException을 발생시킵니다.
  • Serializable을 구현한 클래스에서 serialVersionUID를 명시적으로 선언하지 않은 경우, 직렬화 런타임은 serialVersionUID를 자동으로 생성합니다.
  • 그러나 해당 자동 생성은 컴파일러 구현에 영향을 받을 수 있기 때문에 사용자가 직접 serialVersionUID를 명시적으로 선언하는 것이 권장됩니다.
  • serialVersionUID는 정적 final long 타입으로 선언되어야 합니다.
  • 또한 serialVersionUID는 상속에서 제외하는 것이 권장되기 때문에 private 변수로 선언하는 것이 권장됩니다.
public class File implements Serializable {
    ...
    private static final long serialVersionUID = 301077366599181567L;
    ...
}

출처

0개의 댓글