~전편에 이어서~

ObjectStream을 활용해보자!

 


# 오늘의 코딩 #

[순서]

1. 먼저 내보낼 객체의 기반이 되는 'MyData' Class를 생성 (Serializable interface를 구현한 클래스) 

2. ObjectStream활용을 위한 클래스를 생성하여 write와 read method를 작성/실행해보자

 

#1. MyData Class

- 사용자 정보를 가지고 있으며 직렬화가 막혀있는 변수가 존재함

package test;

import java.io.Serializable;

/**
 * 사용자의 정보를 가진 클래스
 * @author user
 */
public class MyData implements Serializable{
		
	/**
	 * serialVersionUID : 이 클래스 파일이 JVM 외부로 빠져나갔을 때 파일을 검증하기 위한 용도의 Constant
	 * 객체가 JVM 외부로 나갔을 때 검증하기 위해서 ID를 부여함
	 * 1. 현재 JVM에서 빠져나간 객체임을 검증 가능
	 * 2. 시간에 대한 체크 가능
	 */
	private static final long serialVersionUID = -6444648902030740467L;
	private double height;
	private transient double weight; // 직렬화를 막기 위해서는 transient를 걸자 (직렬화 방지)
	private int age;
	private transient String name; // 직렬화 방지
	
	public MyData() {
	}

	public MyData(double height, double weight, int age, String name) {
		this.height = height;
		this.weight = weight;
		this.age = age;
		this.name = name;
	}

	public double getHeight() {
		return height;
	}

	public void setHeight(double height) {
		this.height = height;
	}

	public double getWeight() {
		return weight;
	}

	public void setWeight(double weight) {
		this.weight = weight;
	}

	public int getAge() {
		return age;
	}

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

	public String getName() {
		return name;
	}

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

	@Override
	public String toString() {
		return "MyData [height=" + height + ", weight=" + weight + ", age=" + age + ", name=" + name + "]";
	}
	
}//class

 

#2. UseObjectStream Class

1) MyData Class를 활용하여 객체 내보내기/읽기 Stream을 실행하는 class

package test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 * 객체를 직렬화하여 JVM외부로 내보내거나 읽어들이는 클래스
 */
public class UseObjectStream {

	public void writeObj(MyData md) throws IOException, NotSerializableException {
		// 1. 스트림 생성
		ObjectOutputStream oos = null;
		try {
			oos = new ObjectOutputStream(new FileOutputStream(new File("C:/Users/user/Desktop/obj.txt")));
			// 2. 객체 스트림에 기록
			oos.writeObject(md);
			// 3. 스트림의 내용 분출
			oos.flush();
		} finally {
			// 4. 연결 끊기
			if (oos != null) {
				oos.close();
			}//end if
		}//end finally
	}// writeObj

	public MyData readObj() throws IOException, ClassNotFoundException {
		MyData md = null;

		// 1. 스트림 생성
		ObjectInputStream ois = null;
		try {
			ois = new ObjectInputStream(new FileInputStream(new File("C:/Users/user/Desktop/obj.txt")));
			// 2. 스트림에서 객체 읽기
			md = (MyData) ois.readObject(); // method 반환형이 object이므로 클래스로 casting 해줘야 값을 받아올 수 있음
		} finally {
			// 3. 연결 끊기
			if (ois != null) {
				ois.close();
			} // end if
		} // end fianlly
		return md;
	}// readObj

 

2) main maethod에서 실행해보자!! writeObj를 먼저 실행하면!

	public static void main(String[] args) {

		MyData md = new MyData(180.3, 73.2, 26, "김동동");
		UseObjectStream uos = new UseObjectStream();

		try {
			uos.writeObj(md);
			System.out.println("직렬화된 객체:" + md);
		} catch (NotSerializableException nse) {
			System.out.println("객체가 직렬화되지 않습니다.");
			nse.printStackTrace();
		} catch (IOException ie) {
			System.out.println("객체를 쓰는 동안 문제 발생");
			ie.printStackTrace();
		} // end catch

	}// main

}// class

# 출력 결과 #

- JVM외부로 나간 객체의 내용 & 설정한 경로에 작성된 파일

 

 

3) readObj method도 실행해보자!

		try {
			MyData md1 = uos.readObj();
			System.out.println("역직렬화된 객체:" + md1);
		} catch (ClassNotFoundException cnfe) {
			cnfe.printStackTrace();
		} catch (IOException ie) {
			ie.printStackTrace();
		} // end catch
		
	}// main

}// class

 

# 출력 결과 #

- MyData Class에서 직렬화 방지를 위해 transient로 막아둔 변수는 null로 읽힘 

  >> 해당 변수는 JVM외부로 나가지 않았다라는 것 확인!

 

 1. 직렬화 (Serializable) 

객체를 일정 크기로 쪼개는 것.

- 단, 모든 객체는 직렬화되지 않음 (직렬화 되어져야 하는 객체는 java.io.Serivalizable 인터페이스를 구현한 클래스만 가능)

- Serializable 인터페이스를 구현한 클래스로부터 생성된 instance는 일정 크기로 쪼개질 수 있음

 

* Serializable 인터페이스는 Constant와 abstract method가 없음

   - 목적 : 데이터형에 체크 (is a 관계에 대한 확인용)

   - interface에 상수/추상메소드가 없고 -able로 명명되어있으면 특정한 용도를 확인하기 위한 interface인 경우가 많다!

 

 2. transient 

- 직렬화를 막는 키워드로 변수의 접근지정자 종류

  > 객체가 가진 중요한 정보를 JVM안에서만 사용하도록 만들 수 있음 (JVM밖으로 못 나감)

- 문법)

public class Test implements Serializable{

           int i; // 직렬화 가능

           String str; // 직렬화 가능 (String이 serializable을 implements함)

           transient String str1; // serializable보다 transient가 우선되어 직렬화 막기 가능

           private transient int j; // 직렬화 막기 }

 

 3. ObectStream 

- JVM에 생성된 instance(객체)를 JVM 외부로 내보내거나,

  JVM외부에 존재하는 객체를 JVM내부로 읽어 들일 때 사용하는 Stream

- 기본형 데이터형은 JVM외부로 나갈 수 있으나 instance는 JVM외부로 나갈 수 없음

  > 기본형 데이터형은 크기가 정해져 있어서 쪼갤 수 있음

  > 인스턴스는 클래스가 Serializable 인터페이스를 구현해야 직렬화 가능

- ObjectInputStream, ObjectOutputStream 사용 (8bit Stream)

 

 4. serialVersionUID 

- 이 클래스 파일이 JVM 외부로 빠져나갔을 때 파일을 검증하기 위한 용도의 Constant

- 용도 1) 현재 JVM에서 빠져나간 객체임을 검증 가능

  용도 2) 시간에 대한 체크 가능 (만약 내가 1년뒤에 ID 번호를 바꾸면 읽어들일 때 오류 발생!!)

- 클래스는 직렬화되어서 JVM밖으로 나갔다가 조작될 수 있는 위험(보안상의 위험)이 있기 때문에

  JVM이 serial warinng을 띄움

  > 이를 검증하기 위해 serialVersionUID를 거는 것이다!

- @SuppressWarnings(“serial”) 어노테이션 처리를 해도 되지만 ID부여가 안전함

 

- EX)

private static final long serialVersionUID = -8401830166169521484L; // 내보냈을 때 기존 ID

private static final long serialVersionUID = -8401830166169521485L; // 바뀐 ID

 

이후 .readObject();를 시행하면 아래와 같은 [InvalidClassException]이 발생함!

 

// 콘솔창 오류 내용 :
java.io.InvalidClassException: day0818.MyData; local class incompatible: stream classdesc serialVersionUID = -8401830166169521484, local class serialVersionUID = -8401830166169521485

(ID가 바뀌어서 유효한 클래스가 아니라는 의미! -> 밖에 있는 직렬화된 클래스가 내부로 들어오지 못함)

 

 

 

코딩은 다음편에!

+ Recent posts