Java

[Java] 직렬화(Serialization)와 역직렬화(Deserialization)

구루싸 2020. 8. 20. 22:34
반응형
SMALL

잠시 귀차니즘으로 인해

술을 홀짝 마셨더니

정말 오랜만에

글을 작성하게 되었습니다

장마가 지나니 폭염이 시작되서

마스크가 더욱 괴롭게 느껴지네요

(코로나19 -_-)

아무튼 오늘의 학습 주제는

직렬화와 파일 입출력입니다

우리가 알고 있는 자바의 객체는

상태와 행동이 담겨있습니다

그런데 객체의 상태를 저장하고

싶을 때는 어떻게 할까요?

만약 객체의 상태를

만들어낸 자바 프로그램에서만

사용한다고 하면 직렬화를 사용하고

다른 프로그램에서도 사용한다면

일반 텍스트 파일로 저장하는

방법이 있습니다

(물론 다른 방법들도 있습니다)

먼저 알아야하는 것은

데이터는 스트림(Stream) 형태로

이동하고 자바 입출력 API에는

파일이나 네트워크 소켓과 같은

출발지 또는 목적지로의 연결을

나타내는 연결 스트림(Connection Stream)과

다른 스트림에 연쇄되어야만 쓸 수 있는

연쇄 스트림(Chain Stream)이 있습니다

대충 요런 느낌?!

아무튼 이제 직렬화를 보겠습니다

우선 직렬화를 할 수 있게 하려면

Serializable 인터페이스를 구현합니다

당연한 소리지만

상위 클래스 가운데 하나라도

직렬화할 수 있다면

하위 클래스도 직렬화 가능하고

직렬화하려는 객체와 관련된

모든 것이 저장됩니다

만약 직렬화하려는 객체의 상태에

다른 인스턴스 변수가 있고

그 인스턴스를 직렬화할 수 없다면

NotSerializableException이 발생하게 됩니다

물론 저장할 필요가 없거나

저장해서는 안되는 것은

transient 키워드를 지정합니다

직렬화화면 그 객체에는

그 객체가 속한 클래스의

버전ID(serialVersionUID)가 찍힙니다

직렬화가 있으니

역직렬화(Deserialization)도 있습니다

중요한 점은

역직렬화로 생성된 객체는

생성자가 실행되지 않으며

transient로 지정된 변수는

객체 레퍼런스의 경우에는 null

원시 변수의 경우에는 기본값이

주어진다는 것입니다

만약에 클래스가 변경되었다면

어떻게 될까요?

앞서 언급한 serialVersionUID가

다를 수 있어서

역직렬화를 할 수 없게 만듭니다

그래서 클래스가 변경될 것 같다면

이 버전 ID를 직접 제어합니다

그럼 클래스를 변경했을 때

문제가 생기는 경우와

아닌 경우를 살펴보겠습니다

역직렬화 과정에 문제가 일어날 수 있는 경우

1. 인스턴스 변수를 삭제하는 경우
2. 인스턴스 변수의 유형을 변경하는 경우
3. transient로 지정하지 않았던 변수를 transient로 지정하는 경우
4. 클래스를 상속 계층에서 위나 아래로 옮기는 경우
5. Serializable 클래스를 Serializable이 아닌 클래스로 변경하는 경우
6. 인스턴스 변수를 정적 변수를 변경하는 경우
역직렬화 과정에 문제가 생기지 않는 경우

1. 클래스에 새로운 인스턴스 변수를 추가하는 경우
2. 상속 트리에 클래스를 추가하는 경우
3. 상속 트리에서 클래스를 제거하는 경우
4. 인스턴스 변수의 접근 레벨을 역직렬화 과정에서 변수에 값을 대입하는 데 문제가 없는 범위 내에서 변경하는 경우
5. transient로 지정했던 인스턴스 변수를 transient가 아닌 변수로 변경하는 경우

아래는 직렬화의 간단한 예제입니다

1. 클래스(Class) GameCharacter.java 

package headFirst;
import java.io.*;
public class GameCharacter implements Serializable {
	int power;
	String type;
	String[] weapons;
	
	public GameCharacter(int p, String t, String[] w) {
		power = p;
		type = t;
		weapons = w;
	}
	
	public int getPower() {
		return power;
	}
	
	public String getType() {
		return type;
	}
	
	public String getWeapons() {
		String weaponList = "";
		
		for ( int i = 0; i < weapons.length; ++i ) {
			weaponList += weapons[i] + " ";
		}
		
		return weaponList;
	}
}

2. 클래스(Class) GameServer.java

package headFirst;
import java.io.*;
public class GameServer {
	public static void main(String[] args) {
		GameCharacter one = new GameCharacter(50, "Elf", new String[] { "bow", "sword", "dust" } );
		GameCharacter two = new GameCharacter(200, "Troll", new String[] { "bare hands", "big ax" } );
		GameCharacter three = new GameCharacter(120, "Magician", new String[] { "spells", "invisibility" } );
		try {
			ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("Game.ser"));
			os.writeObject(one);
			os.writeObject(two);
			os.writeObject(three);
			os.close();
		} catch ( IOException e ) {
			e.printStackTrace();
		} catch ( Exception e ) {
			e.printStackTrace();
		}
		
		one = null;
		two = null;
		three = null;
		
		try {
			ObjectInputStream is = new ObjectInputStream(new FileInputStream("Game.ser"));
			GameCharacter oneRestore = (GameCharacter) is.readObject();
			GameCharacter twoRestore = (GameCharacter) is.readObject();
			GameCharacter threeRestore = (GameCharacter) is.readObject();
			
			System.out.println("One's type : " + oneRestore.getType());
			System.out.println("Two's type : " + twoRestore.getType());
			System.out.println("Three's type : " + threeRestore.getType());
			
			is.close();
		} catch ( IOException e ) {
			e.printStackTrace();
		} catch ( Exception e ) {
			e.printStackTrace();
		}
	}
}

직렬화를 통한

객체를 저장하는 방법 외에

다른 프로그램에서도

읽을 수 있도록 데이터를

일반 텍스트 파일로

저장하는 방법은

파일 입출력에 대해

학습할 기회가 있으면

알아보도록 하겠습니다

이것으로 이번 학습을

마치겠습니다

그럼 이만-_-

 

반응형
LIST