will come true

[Java] 직렬화 (Serialization) 본문

Java

[Java] 직렬화 (Serialization)

haehyun 2023. 5. 9. 00:31

직렬화(Serialization)

객체를 데이터 스트림으로 만드는 것.
객체에 저장된 데이터를 스트림에 쓰기(write)위해 연속적인(serial) 데이터로 변화하는 것을 말한다.
반대로 스트림으로부터 데이터를 읽어서 객체를 만드는 것을 역직렬화(deserialization)라고 한다.

객체
(개발용)

직렬화
(데이터를 저장한다)
스트림
(데이터 보관용)

역직렬화
(저장된 데이터를 읽어들인다)

 

객체를 저장하는 방법

실제로 객체는 클래스에 정의된 인스턴스 변수의 집합일 뿐, 클래스변수나 메서드는 포함되지 않는다.
인스턴스 변수는 인스턴스마다 다른 값을 가질 수 있어야하기 때문에 별도의 메모리 공간이 필요하지만 메서드는 변하는 것이 아니라서 메모리를 낭비해 가면서 인스턴스마다 같은 내용의 코드(메서드 정의)를 포함시킬 이유가 없다.

따라서 어떤 객체를 저장하고자 한다면, 현재 객체의 모든 인스턴스 변수의 값을 저장하기만 하면 된다. 그리고 저장했던 객체를 다시 생성하려면 그 값들을 읽어들여 도로 인스턴스 변수에 대입해주면 되는 것이다.

ObjectInputStream, ObjectOutputStream

  • ObjectInputStream : 직렬화(스트림에 객체를 출력)
  • ObjectOutputStream : 역직렬화(스트림으로부터 객체를 입력)

각각 InputStream, OutputStream을 상속받지만 기반스트림을 필요로 하는 보조스트림이다. 그래서 객체를 생성할 때 입출력(직렬화/역직렬화)할 스트림을 지정해주어야 한다.

ObjectInputStream(InputStream in)
ObjectOutputStream(OutputStream out)

 

ObjectoutputStream 직렬화 (객체 → 스트림)

// objectfile.ser 파일에 UserInfo객체를 직렬화하여 저장한다.
FileOutputStream fos = new FileOutputStream("objectfile.ser");
ObjectoutputStream oos = new ObjectOutputStream(fos);

// 객체를 출력하면, 객체가 파일에 직렬화되어 저장된다.
out.writeObject(new UserInfo());

 

ObjectInputStream 역직렬화 (스트림 → 객체)

// objectfile.ser 파일을 읽어들인 후 역직렬화해서 객체로 저장한다.
FileInputStream fis = new FileInputStream("objectfile.ser");
ObjectInputStream ois = new ObjectInputStream(fis);

UserInfo info = (UserInfo)in.readObject();

 

객체를 직렬화/역직렬화 하는 작업은 객체의 모든 인스턴스 변수가 참조하고 있는 모든 객체에 대한 것이기 때문에 상당히 복잡하며 시간도 오래 걸린다. readObject()와  wrtieObject()를 사용한 자동직렬화가 편하긴 하지만 직렬화 작업시간을 단축시키려면 직렬화하고자 하는 객체의 클래스에 추가적으로 다음과 같은 2개의 메서드를 직접 구현해줘야 한다.

private void wrtieObject(ObjectOuputStream out) throws IOException {
	// write메서드를 사용해서 직렬화를 수행한다.
}

private void readObject(ObjectInputStream in) thorws IOException, ClassNotfoundException {
	// read메서드를 사용해서 역직렬화를 수행한다.
}

 

직렬화가 가능한 클래스 만들기

직렬화하고자 하는 클래스가 java.io.Serializable 인터페이스를 구현하도록 한다.
Seriablizable 인터페이스는 아무런 내용이 없는 빈 인터페이스지만, 직렬화를 고려하여 작성한 클래스인지를 판단하는 기준이 된다.

이때 Serializable을 구현한 클래스를 상속받는다면, Serializable을 구현하지 않아도 된다. Seriablizable을 구현함으로써 객체를 직렬화하면 클래스내에 정의된 인스턴스 변수들도 함께 직렬화되는데 자식 클래스는 부모클래스의 인스턴스 변수를 그대로 물려받기 때문에 자식클래스는 Seriablizable을 구현하지 않아도 이미 변수가 직렬화 대상에 포함되어 있다.

 

상속관계에서 직렬화

직렬화 하려는 객체의 부모클래스가 Serializable을 구현하지 않았다면 자식클래스를 직렬화할 때 부모클래스에 정의된 인스턴스 변수는 직렬화 대상에서 제외된다.

public class SuperClass {
    String name;		// 직렬화x
    String password;	// 직렬화x
}

public class SubClass extends SuperClass implements Serializable {
	int age;	// 직렬화o
}

 

Object 직렬화 예외

java.io.NoSerializableException

직렬화할 수 없는 클래스의 객체를 인스턴스변수가 참조하고 있기 때문에 직렬화에 실패했다는 예외.
직렬화하려는 클래스의 인스턴스 변수로 Object타입이 존재할 때 발생한다. Object는 Serializable을 구현하지 않았기 때문에 직렬화할 수 없기 때문이다.
물론 직렬화 여부는 인스턴스변수의 타입이 아니라 실제로 연결된 객체의 종류에 의해서 결정되기 때문에 Object obj = new String("abc"); 에서 String은 직렬화될 수 있다.

public class UserInfo implements Serializable {
	String name;
    String password;
    int age;
    Object obj = new Object();	// 직렬화x
}

 

transient 제어자

직렬화하고자 하는 객체의 클래스에 직렬화가 안 되는 객체에 대한 참조를 포함하고 있다면, 제어자 transient를 붙여수 직렬화 대상에서 제외되도록 할 수 있다. 주로 패스워드와 같이 보안상 직렬화되면 안 되는 값에 대해서 transient를 사용할 수 있다. transient가 붙은 인스턴스의 값은 그 타입의 기본값(0, null)으로 직렬화된다.

dat 파일

.dat 파일 확장자를 가진 파일은 만든 프로그램과 관련된 특정 정보를 저장하는 일반 데이터 파일이다.
dat 파일에는 소프트웨어가 처리해야 하는 중요한 정보들이 있으며 프로그램에서 열고 참조하며 사용한다.

ArrayList 직렬화

ArrayList와 같은 리스트 타입 객체도 직렬화 가능하다. ArrayList 클래스가 java.io.Serializable을 구현하고 있기 때문이다.
보통 객체를 역직렬화 할 때는 직렬화할 때의 순서와 일치해야 하는데 ArrayList 하나만 역직렬화 하면 되므로 역직렬화할 객체의 순서를 고려하지 않아도 되기 때문에 직렬화할 객체가 많을 때는 각 객체를 직렬화 하는 것보다 ArrayList 와 같은 컬렉션에 저장해서 직렬화하는 것이 좋다.

public class ArrayList extends AbstractList 
			implements List, RandomAccess, Cloneable, java.io.Serializable {
	transient Object[] elementData;	// Object 배열 직렬화 제외시키기

}

 

추가예정

Comments