Serialization 이란?
- 인스턴스의 상태를 그대로 파일 저장하거나 네트워크로 전송하고 (serialization) 이를 다시 복원(deserialization) 하는 방식
- 자바에서는 보조 스트림을 활용하여 직렬화를 제공
ObjectInputStream
과ObjectOutputStream
Serializable 인터페이스
- 직렬화는 인스턴스의 내용이 외부로 유출되는 것이므로 프로그래머가 해당 객체에 대한 직렬화 의도를 표시해야 한다.
- 구현 코드가 없는 marker interface
- transient : 직렬화 하지 않으려는 멤버 변수에 사용 (Socket 등 직렬화 할 수 없는 객체)
import java.io.*;
class Person {
String name;
String job;
public Person() {
}
public Person(String name, String job) {
this.name = name;
this.job = job;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", job='" + job + '\'' +
'}';
}
}
public class SerializationTest {
public static void main(String[] args) {
Person personLee = new Person("이순신", "대표이사");
Person personKim = new Person("김유신", "상무이사");
try (FileOutputStream fos = new FileOutputStream("serial.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
// serialization
oos.writeObject(personLee);
oos.writeObject(personKim);
} catch (IOException e) {
e.printStackTrace();
}
try (FileInputStream fis = new FileInputStream("serial.txt");
ObjectInputStream ois = new ObjectInputStream(fis)) {
// deserialization
Person pLee = (Person) ois.readObject();
Person pKim = (Person) ois.readObject();
System.out.println("pLee = " + pLee);
System.out.println("pKim = " + pKim);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
writeObject()
로 serialization, readObject()
로 deserialization 을 구현하고 있다.
위 코드를 실행해보자.
writeObject()
를 실행할 때 직렬화를 할 수 없다. 직렬화를 하기 위해서는 serializable interface 를 명시해줘야하기 때문이다.
class Person implements Serializable { ... }
이렇게 Serializable interface 를 구현하겠다고 명시해줘야 정상 동작한다.
생성된 파일을 보면 요상한 문자들이 써있는데 이것이 바로 정보를 직렬화한 것이며, deserialization 과정을 통해 정보를 복원하고 읽을 수 있게 된다.
그렇다면 Serializable interface 는 어떤 인터페이스이길래 이런 작업을 가능하게끔 하고 왜 명시해줘야하는걸까?
호기심을 잔뜩 억누르며 코드를 뜯어봤다.
아무 것도 없었다...!
이게 도대체 어떻게 된 걸까?
Marker Interface
단지 Serializable interface 를 구현하고 있는 객체가 직렬화 가능하다고 표시해주는 역할만 하며, 아무 것도 구현하지 않고 단지 명시해주기 위해 사용되는 인터페이스를 "마커 인터페이스" 라고 한다.
transient
Socket 클래스는 직렬화할 수 없다. 그리고 만약 직렬화를 원하지 않는 필드가 있다면 transient
키워드를 사용해서 무시할 수 있다.
class Person implements Serializable {
String name;
transient String job;
...
}
transient
를 사용하면 기본값이 들어가게 된다.
만약 직접 직렬화를 구현하고 싶다면, Externalizable interface 를 구현하여 사용하면 된다.