개요
- 2017년에 정리했었던 문서를 다시 한번 정리해둔다.
- 직렬화 : 메모리 상에 존재하는 객체를 어떤 데이터 포맷으로 변환하는 것 (나중에 쓰기위해)
- 직렬화 개념은 Java에만 있는가? 아니다. 직렬화는 대부분의 언어에서 지원한다.
- 역직렬화는 반대과정이다. 데이터를 입력받아서 객체를 다시 만들어내는 것이다.
왜 필요한가? / 언제쓰이는가?
- 데이터로서 보존할 때
- 통신할 때
- 예를들면, JSON 이나 XML형식으로 표현된 문자열도 직렬화의 한 예이다.
- 또한, 각 언어별로 직렬화 메카니즘이 존재. 이 때는 별도의 포맷으로 변환된다.
JAVA에서 직렬화/역직렬화하는 방법
직렬화 대상 클래스 정의
package serialization;
import java.io.Serializable;
public class User implements Serializable{
private String name;
private String password;
private String email;
private int age;
public User(String name, String password, String email, int age){
this.name = name;
this.password = password;
this.email = email;
this.age = age;
}
public String toString(){
return "(" + name +", " + password + ", " + email + ", " + age + ")";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
직렬화 테스트 클래스
package serialization;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
public class TestClass {
private static final String path = "user.ser"; //프로젝트 루트에 저장됨.
public static void main(String[] args) {
serialize();
deserialize();
}
public static void serialize(){
try{
FileOutputStream fos = new FileOutputStream(path);
BufferedOutputStream bos = new BufferedOutputStream(fos);
ObjectOutputStream out = new ObjectOutputStream(bos);
User u1 = new User("User1", "1234", "ldd@naver.com", 30);
User u2 = new User("User2", "5678", "teser@gmail.com", 25);
ArrayList list =new ArrayList<>();
list.add(u1);
list.add(u2);
out.writeObject(u1);
out.writeObject(u2);
out.writeObject(list);
out.close();
System.out.println("직렬화 완료");
}catch(Exception e){
e.printStackTrace();
}
}
public static void deserialize(){
try {
FileInputStream fis = new FileInputStream(path);
BufferedInputStream bis = new BufferedInputStream(fis);
ObjectInputStream in = new ObjectInputStream(bis);
User u1 = (User) in.readObject();
User u2 = (User) in.readObject();
ArrayList list = (ArrayList) in.readObject();
System.out.println(u1.toString());
System.out.println(u2.toString());
System.out.println("count : " + list.size());
System.out.println(list.toString());
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
실행결과
직렬화된 파일은 다음과 같이 생겼다.
헥스 에디터로 열어보면 다음과 같이 생겼다.
직렬화의 특징
- 직렬화시킬 때 다양한 오브젝트를 함께 직렬화할 수 있다.
- 예제에서는 User오브젝트 두 개와 User오브젝트가 들어가 있는 ArrayList 오브젝트를 직렬화하였다.
직렬화된 객체 바이너리 분석
- 바이너리에 클래스에 대한 각종 정보를 나타내는 플래그가 포함된다. 필드의 타입, 직렬화되는 필드의 개수 등이다.
- 순서는 필드명, 타입, 값 순으로 저장된다.
- 바이트열을 보면
AC ED 00 05 73 72
순으로 되어 있다. AC ED
는 “매직넘버” 라고 불리는 것이다. Java로 직렬화된 객체는 항상AC ED
로 시작된다. 이를 통해 Java 직렬화 오브젝트인 것을 알 수 있다.00 05
는 버전을 나타낸다.-
73 72
는 CONTENT ELEMENT (TC_OBJECT, TC_CLASSDESC)라고 한다. 나머지 데이터의 구조를 추론하는데에 쓰인다. - 중요한 것은 스트림이 저장되는 것에도 분명히 문법이 있다는 것.
- 스트림에 쓰여진 각 오브젝트들은 핸들을 부여받는다.
-
핸들은 0x7E0000부터 시작한다. (스트림이 리셋되면 0x7E0000부터 다시 시작)
- 스트림안에 저장된 오브젝트는 로드되면 바로 인스턴스화 된다. 모든 스트림이 해석되기 전에…이 말은 페이로드는 검증전에 역직렬화되고 실행된다는 것이다. 위험하다!
참고
- https://ko.wikipedia.org/wiki/%EC%A7%81%EB%A0%AC%ED%99%94