본문 바로가기
JAVA SE/이론 및 개념

[20.07.31/Day_17] Java SE / IO(Object Serialization 객체 직렬화), serialVersionUID, transient

by 파프리카_ 2020. 7. 31.
728x90
반응형

[ Review 복습 ]

 

Stream

 

java.io의 대표 abstract class

  Byte Stream
1 byte (음원, 이미지 등)
Character Stream
2 byte (텍스트 등)
입력 InputStream Reader
출력 OutputStream Write

 

 

Node Stream : 연결되는 스트림(없으면 생성), socat

Processing Stream : 기능 동작하는 스트림(보조, 부가기능)

 

[코딩 패턴]

  Node Stream  Processing Stream
Output new FileWriter  new PrintWriter | .println() 출력 | .close()
Input new FileReader  new BufferedReader | .readLine()  읽기(입력) | .close()

* 자세한 설명 : https://creamilk88.tistory.com/38

 


[ IO ]

객체 직렬화 ( Object Serialization )

 : 메모리 상에 있는 객체 정보를 외부로 전송할 수 있는 상태로 만드는 것

 

java.io.Serializable

 

객체 직렬화와 역직렬화의 구조

> 객체 직렬화를 위해서는 해당 객체는 java.io.Serialization interface를 implements 해야 한다.

즉, 해당 객체는 Serializable 계층구조의 하위 객체여야 한다.

(그렇지 않으면, jvm이 java.io.NotSerializableException을 발생시킴)

 

> 객체 직렬화 시 사용할 스트림

ObjectOutputSream - writeObject(Object o)

> 객체 역직렬화 시 사용할 스트림

ObjectInputStream - readObject() : Object

 

 

객체 직렬화 예제

 

1. Output 예제

package step1;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

import model.Person;

public class TestObjectOutput {	
	public static void main(String[] args) {
		String savePath = "C:\\kosta203\\iotest\\person.obj";
		
		try
		{
		// 1. 경로(savePath)에 있는 파일 읽기(연결) & person.obj 파일 생성
		FileOutputStream fos = new FileOutputStream(savePath);
		
		// 2. Person 객체를 직렬화하여 제공
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		
		// 3. Person object 정보를  person.obj 파일에 입력
		Person p = new Person("아이유", "판교", "자바킹");
		oos.writeObject(p);
		
		// 4. exception 없을 시 출력
		System.out.println("객체 직렬화하여 person.obj에 Person p 저장 완료");
		
		// 5. output object 닫기
		oos.close();
		}
		
		catch (FileNotFoundException fe)
		{
			fe.printStackTrace();
		}
		
		catch (IOException e) 
		{
			e.printStackTrace();
		}
	}
}

[ Node Stream  : 장치에 연결 ]

1. FileOutputStream은 파일에 연결

← FileOutputStream fos = new FileOutputStream(savePath);

 

[ Processing Stream  (보조 스트림) : 다양한 기능을 지원 

2. ObjectOutputStream은 객체 직렬화해 제공

← ObjectOutputStream oos = new ObjectOutputStream(fos);

3. Person object 정보를 Object stream(oos)에 입력하여 출력

←Person2 p2 = new Person2("아이유", "판교", "자바킹");

oos.writeObject(p2);

 

객체 직렬화가 되지 않아, exception 발생

 

model object를 객체 직렬화해주기 위해 Serializable interface를 implement해주어야 함

 Serializable interface를 implements 해주어야만 직렬화 가능한 객체가 된다.

package model;

import java.io.Serializable;

//super class
public class Person implements Serializable{
	/**
	 * serialVersionUID : class의 지문
	 * 클래스 버전을 명시함으로써, 객체 직렬화와 역직렬화 시 호환성을 위해 사용
	 */
	private static final long serialVersionUID = 8434327299042305734L;
	private String name;
	private String address;
	private String password;
	
	public Person(String name, String address, String password) {
		this.name = name;
		this.address = address;
		this.password = password;
	}

	public String getName() {
		return name;
	}

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

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
	
	
}

정상 수행 됨.

 

 

2. Input 예제

package step1;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;

import model.Person;

public class TestObjectInput {
	public static void main(String[] args) {
		String savePath = "C:\\kosta203\\iotest\\person.obj";
		
		try
		{
		// 1. 경로(savePath)에 있는 파일 가져오기(연결)
		FileInputStream fis = new FileInputStream(savePath);
		
		// 2. input object stream(object) 객체 생성
		ObjectInputStream ois = new ObjectInputStream(fis);
		
		// 3. input object(ois)에 담긴 정보 : Object type으로 반환
		// 타입을 Object -> Person로 downcasting 필요
		Person p = (Person)ois.readObject();
		
		// 4. 정보 하나씩 출력
		System.out.println(p.getName());
		System.out.println(p.getAddress());
		System.out.println(p.getPassword());
		/* 출력값:
		 * 아이유
		 * 판교
		 * 자바킹
		 */
		
		// 5. input object 닫기
		ois.close();
		}
		
		catch (FileNotFoundException fe)
		{
			fe.printStackTrace();
		}
		
		catch (IOException e) 
		{
			e.printStackTrace();
		}
		
		catch (ClassNotFoundException ce) {
			ce.printStackTrace();
		}
	}
}

Person object가 이미 객체직렬화되어있어, 문제없이 수행된다.


> serialVersionUID

 : class의 지문 

 : 클래스 버전을 명시함으로써, 객체 직렬화와 역직렬화 시 호환성을 위해 사용

(private static final long serialVersionUID = 8434327299042305734L;)

: 정보를 가져올 클래스(A)가 변경되더라도, 오류가 나지않고  Input class에서 A에서 변경된 사항이 적용되어 실행된다.

 

 : 직렬화 대상 클래스 (Serializable 을 implements하는 클래스)들은 개별 클래스마다 JVM에 의해 자신의 serialVersionUID를 가진다. 이 때, 클래스의 정보(인스턴스 변수)가 변경되면 다시 해당 클래스의 serialVersionUID가 재할당된다.

이 경우에서 클래스의 정보가 업데이트 될 경우 기존의 클래스로 직렬화된 정보를 역직렬화하여 입력받으려 할 경우 Exception이 발생하게 된다. 이를 해결하기 위하여, 클래스 업데이트에 따른 직렬화와 역직렬화의 호환성을 위해 serialVersionUID를 직접 명시할 것을 권장한다. 


> transient keyword [트랜지언t]

: 변수 앞에 지정하는 modifier

: 객체 직렬화 대상에서 해당 인스턴스 변수의 정보를 제외할 때 사용

(객체가 직렬화되어 외부로 전송될 때, 특정 인스턴스 변수의 정보를 직렬화 대상에서 제외하고 할 때, transient 키워드를 이용한다.)

 

transient 사용 예제

 

/ object class

package model;

import java.io.Serializable;

public class Person2 implements Serializable{
	private String name;
	private String address;
	private transient String password;
	
	public Person2(String name, String address, String password) {
		this.name = name;
		this.address = address;
		this.password = password;
	}

	public String getName() {
		return name;
	}

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

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
	
	
}

 

 object의 password변수를  transient modifier 로 지정 : 직렬화 대상에서 제외

← private transient String password;

 

/ input class

package step1;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;

import model.Person2;

public class TestObjectInput {
	public static void main(String[] args) {
		String savePath = "C:\\kosta203\\iotest\\person.obj";
		
		try
		{
		// 1. 경로(savePath)에 있는 파일 가져오기(연결)
		FileInputStream fis = new FileInputStream(savePath);
		
		// 2. input object stream(file) 객체 생성
		ObjectInputStream ois = new ObjectInputStream(fis);
		
		// 3. input object(ois)에 담긴 정보 : Object type으로 반환
		// Object -> Person로 downcasting 필요
		Person2 p = (Person2)ois.readObject();
		
		// 4. 정보 하나씩 출력
		System.out.println(p.getName());
		System.out.println(p.getAddress());
		System.out.println(p.getPassword()); 
		/* 출력값:
		 * 아이유
		 * 판교
		 * null
		 */
		
		// 5. input object 닫기
		ois.close();
		}
		
		catch (FileNotFoundException fe)
		{
			fe.printStackTrace();
		}
		
		catch (IOException e) 
		{
			e.printStackTrace();
		}
		
		catch (ClassNotFoundException ce) {
			ce.printStackTrace();
		}
	}
}

password는 transient modifier가 지정되어 있어 직렬화 대상에서 제외되었으므로,

null이 반환된다.


 

728x90
반응형