직렬화(Serialization)

- 직렬화란 자바 시스템 내부에서 사용되는 객체 또는 데이터를 자바 외부에서도 사용할 수 있도록 바이트(byte)단위로 변환하는 것을 말한다.

- 직렬화가 가능한 자료형 및 객체

  기본형 타입 (boolean, char, byte, short, int, long, float, double)

  Serializable 인터페이스를 구현한 클래스의 객체

  transient가 사용된 멤버변수(null 전송), static 멤버변수, 메소드는 직렬화 대상에서 제외된다.

 

역 직렬화(Deserialization)

- 직렬화된 바이트(byte)단위의 데이터를 다시 원래 데이터로 복원하는 것을 말한다.

 

java.io.Serializable 인터페이스

- 객체를 직렬화하기 위해서는 먼저, 객체를 직렬화가 가능하도록 Serializable 인터페이스를 구현해야 한다.

- 이 인터페이스는 객체 직렬화가 제공되어야 함을 자바 가상 머신에 알려주는 역할을 한다.

- Serializable 인터페이스는 다른 인터페이스와는 달리 구현해야 할 메소드가 없으므로 단지 Serializable 인터페이스만 implements 하면 된다.

- 객체의 직렬화 대상에서 제외되는 것은 멤버 변수가 static으로 선언된 경우, 멤버 변수가 transient로 선언된 경우와 메소드이다.

 

형식

public class 클래스명 implements Serializable {

 // 클래스 정의 부분

}

 

transient

- 직렬화 대상에서 제외할 멤버 변수는 transient 키워드를 사용하여 제외한다.

- transient 키워드가 붙은 멤버 변수가 복원되면, 숫자변수는 0, 객체는 null로 설정된다.

public class 클래스명 implements Serializable {

 접근제어자 자료형 변수1;

 접근제어자 transient 자료형 변수2;

 // 클래스 구현

}

 

SerialVersionUID 필드

- serialVersionUID 는 직렬화에 사용되는 고유 아이디로, 선언하지 않으면 JVM에서 디폴트로 자동생성된다.

  serialVersionUID는 역 직렬화 해서 읽어 들일 때 캐스팅한 클래스의 serialVersionUID와 역직렬화 한 serialVersionUID가 맞는지 확인하기 위해 사용된다.

- 선언하지 않아도 동작하는데 문제는 없지만, JVM에 의한 디폴트 serialVersionUID 계산은 클래스의 세부 사항을 매우 민감하게 반영하기 때문에 컴파일러 구현체에 따라서 달라질 수 있어 역 직렬화(deserialization) 과정에서 예상하지 못한 InvalidClassException을 유발할 수 있다. 

- Java에서는 명시적으로 serialVersionUID를 선언할 것을 적극 권장하고 있다.

- 선언 예

private static final long serialVersionUID = 1L;

 

예시>>

더보기

Main

import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class Ex004_transient {
	public static void main(String[] args) {
		UserService ov = new UserService();
		ov.saveFile();
		ov.loadFile();
	}
}

 

Class

// Serializable : 직렬화
// 직렬화에서 제외되는 것 : 메소드, static 변수, transient 변수

class UserDTO implements Serializable { // 바이트로 고쳐서 저장해줌
	private static final long serialVersionUID = 1L; // 같은 클래스인지아닌지 확인용도
	
	private String name;
	private transient String tel; // 직렬화에서 제외됨. 네트워트로 전송이 되지 않음.
	private int age;

	public UserDTO() {
		
	}
	
	public UserDTO(String name, String tel, int age) {
		this.name = name;
		this.tel = tel;
		this.age = age;
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getTel() {
		return tel;
	}

	public void setTel(String tel) {
		this.tel = tel;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
}

class UserService {
	private String pathname = "object2.txt";
	
	public void loadFile() {
		try( ObjectInputStream ois = new ObjectInputStream(new FileInputStream(pathname))) {
			
			System.out.println("파일 내용...");
			while(true) {
				UserDTO vo = (UserDTO) ois.readObject(); // 역직렬화수행
				System.out.println(vo.getName()+"\t"+vo.getTel()+"\t"+vo.getAge());
			}
			
		} catch (EOFException e) {
			// ObjectInputStream 스트림은 파일의 내용을 더 이상 읽을 수 없으면
			// EOFException 예외가 발생한다. 따라서 EOFException 예외를 catch하고
			// 아무런 코드도 작성하지 않는다. // 안잡으면 터짐
		}catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void saveFile() {
		// ObjectOutputStream : Serializable 인터페이스가 구현되어 있어야 저장가능하다.
		try ( ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(pathname))){

			oos.writeObject(new UserDTO("다자바", "010-0000-0000", 20));
			oos.writeObject(new UserDTO("너자바", "010-1111-0000", 20));
			oos.writeObject(new UserDTO("구자바", "010-2222-0000", 20));
			
			System.out.println("파일 저장 완료...");
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

+ Recent posts