ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • serialization(직렬화)
    language/JAVA 2019. 1. 23. 14:01
    반응형

    마지막으로 우리가 만든 객체를 저장하는 것에 대해 생각해 보도록 하겠습니다.


    객체에서 저장되어야 할 것은 객체의 현재 상태일 것입니다. 그리고 그 객체의 상태를 나타내는것은 인스턴스 변수 입니다.


    자바는 직렬화(serialization)을 이용해서 객체의 인스턴스변수들을 byte 형태로 변환해서 저장하게 됩니다. 이 byte 형태의 데이터를 다시 객체로 바꾸는 것을 역직렬화라고 합니다.


    사실 직렬화 하지 않고 파일을 텍스트 형식이나 다른 방법으로 저장할 수도 있습니다.


    하지만 직렬화를 이용하게 되면 아무리 복잡한 클래스라도 기본 조건만 지키면 쉽게 직렬화를 할 수 있고 자바 애플리케이션에서 쉽게 역직렬화 할 수 있다는 장점이 있습니다.


    그렇게 역직렬화가 되게 되면 다시 원래의 객체처럼 쉽게 사용할 수 있게 됩니다 그래서 개발자 입장에서 상당히 편해집니다.


    하지만 직렬화에 시간이 필요하고 여러가지 메타정보를 가지기 때문에 다른 데이터 포맷(json)에 비해 용량이 크다는 단점이 있습니다. 또한 클래스의 내용이 변경되는 경우 역시 주의해야 합니다.(인스턴스 변수 추가 등)


    객체를 직렬화 할 수 있게 하고 싶다면 Serializable 인터페이스를 구현해야 합니다. 이 인터페이스는 구현해야 하는 메소드는 없지만 JVM에게 직렬화 할 수 있는 것을 알려줍니다.


    그러면 객체를 저장하는 과정을 보도록 하겠습니다.


    일단은 FileOutputStream 객체를 만듭니다. FileOutputStream은 바이트단위로 출력을 내보내는 역할을 합니다.


    이때 인자로 들어가는 문자열은 저장파일의 경로가 되고 해당하는 파일이 없으면 새로운 파일을 만들게 됩니다.


    이렇게 Stream 객체에는 목적지가 필요합니다.


    그리고 ObjectOutputStream을 연결합니다.


    ObjectOutputStream은 객체를 직렬화 하는 역할을 하게 됩니다. 그리고 FileOutputStream을 통해 바이트 형태로 저장되게 되는 것입니다.


    그리고 ObjectOutputStream 객체에 저장할 객체를 씁니다.


    만약 직렬화가 되는 객체에서 인스턴스 변수가 다른 객체를 참조하고 있을 수 있습니다.


    객체가 직렬화되면 인스턴스 변수로 참조되는 모든 객체 또한 직렬화됩니다. 그리고 그렇게 직렬화된 객체에서 참조하는 다른 모든 객체도 직렬화됩니다.


    그런데 Serializable을 구현하지 않은 클래스나 저장되어야 하지 말아야 할 객체가 있는 경우에는 이렇게 자동으로 직렬화 되어서는 안됩니다.


    또 스레드나 네트워크 연결 같이 프로그램을 실행하는 그시점에서만 적용할 수 있는 객체들은 저장의 대상이 되지 못합니다.


    그때는 저장해서는 안되는 인스턴스 변수 앞에 transient 키워드를 붙여서 직렬화 할 때 저장하지 않고 넘어가라고 알려주면 됩니다.


    먼저 FileInputStream 객체를 만듭니다. 이 때 인자값으로 전달된 문자열은 읽을 파일의 경로입니다. 만약 존재하지 않는다면 예외가 발생하게 됩니다.


    그리고 ObjectInputStream 객체에서는 FileInputStream 객체에서 받아온 객체를 읽어올 준비를 합니다.


    그리고 readObject를 통해 객체를 받아옵니다.


    이 과정에서 JVM은 직렬화된 객체와 함께 저장된 정보를 통해 객체의 클래스 유형을 결정합니다.


    JVM에서 객체의 클래스를 찾아서 불러오려는 시도를 한 이후 클래스를 찾거나 불러오는 데 실패한다면 예외가 발생합니다.


    새로운 객체는 힙에 공간을 할당받지만 직렬화된 객체의 생성자가 실생 되는 것은 아닙니다. 생성자가 실행되면 객체가 초기화 되기 때문입니다.


    이 객체를 필요한 클래스 유형 변수에 참조시키면 저장된 객체를 다시 사용할 수 있게 됩니다.


    transient로 지정된 변수의 경우에는 기본값이 주어집니다.


    그런데 만약 저장된 객체를 불러왔는데


    해당하는 클래스의 내용이 달라졌다고 생각 해 보겠습니다.


    앞서 사용한 Member 클래스를 이렇게 변경한다고 생각해 봅시다.


    보통 우리는 이런 상황에서 새롭게 추가된 phone이 null값을 가진다고 해도 원래 존재하던 변수는 값이 채워지길 원합니다.


    하지만 이런 경우에 JVM InvalidClassException을 발생시키게 됩니다.


    JVM은 serialVersionUID 라는 값을 통해 직렬화 클래스의 버전을 관리하고 있습니다.


    특별히 지정해 주지 않는다면 해쉬값을 가지게 됩니다.


    그리고 클래스의 내용이 약간이라도 변하게 되면 이 값 역시 달라지게 됩니다.


    그래서 InvalidClassException이 발생한 것입니다.


    이것을 피하기 위해서 직접 코드에 serialVersionUID를 지정해 주고 개발자가 직접 관리하는 것이 더 편할 수 있습니다.


    하지만 여전히 예외의 가능성이 남아 있습니다.


    인스턴스 변수의 이름은 같은데 타입이 달라지게 되면 예외가 발생하게 됩니다.


    이 때 int 타입을 long으로 받는다거나 하는 식으로 더 큰 자료형으로 받아도 예외가 발생하게 됩니다.


    그렇기 때문에 항상 자료형에 유의해야 합니다.





    반응형

    'language > JAVA' 카테고리의 다른 글

    스레드 Thread  (0) 2019.01.30
    네트워크, 소켓  (0) 2019.01.30
    GUI / innerclass(내부클래스)  (0) 2019.01.23
    예외 처리(Exception)  (0) 2019.01.23
    정적 변수, 정적 메소드(static)  (0) 2019.01.23
Designed by Tistory.