Notice
Recent Posts
Recent Comments
Link
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
Archives
Today
Total
관리 메뉴

Everything has an expiration date

★★★ 177 - Java 객체 직렬화(Object Serialization) : Hashtable을 객체 직렬화를 통해 파일로 내보내고, 내보낸 파일을 다시 역직렬화로 불러들이는 프로그램(완 전 중 요 해) 본문

[Java]/Program source (java)

★★★ 177 - Java 객체 직렬화(Object Serialization) : Hashtable을 객체 직렬화를 통해 파일로 내보내고, 내보낸 파일을 다시 역직렬화로 불러들이는 프로그램(완 전 중 요 해)

Jelly-fish 2023. 9. 25. 17:49
/* ===================================================
  ■■■ 객체 직렬화(Object Serialization) ■■■
=====================================================*/


/*
○ 객체 직렬화(Object Serialization)는

   메모리에 생성된 클래스 객체의 멤버 변수의 현재 상태를
   그대로 보존해서 파일에 저장하거나
   네트워크를 통해 전달할 수 있는 기능으로
   멤버 변수의 값을 보존한다거나
   다른 네트워크에 있는 호스트에 값을 보낼 경우 사용하게 된다.

   즉, 객체 직렬화는 내용물을 바이트 단위로 변환하여
   파일 또는 네트워크를 통해 송수신(스트림)이 가능하게
   만들어주는 것으로 이 때, 객체란 멤버 변수의 메모리만으로 구성된 것을 말한다.

   따라서, 메소드와 생성자는 객체 직렬화의 대상에서 제외된다.


○ 객체 직렬화의 장점

   객체 자체의 내용을 입출력 형식에 구애받지 않고
   객체를 파일에 저장함으로써 영속성을 제공할 수 있으며
   객체 자체를 네트워크를 통해 손쉽게 교환(송수신)할 수 있게 된다.
                                       ̄ ̄→ (전송 및 수신)

   객체 직렬화는 자바 1.1 이후에 도입되었는데
   그 이유는 RMI 와 Bean 때문이었다.

   RMI 는 원래 객체 통신을 지원해야 하기 때문에
   객체가 그대로(특정 상태를 유지한 채로) 이동할 수 있어야 한다.
   따라서 이를 지원하기 위해서는 객체 직렬화가 필수적이었다.

   또한, Bean 은 설계 시 상태에 대한 정보를 저장할 때
   이 객체 직렬화를 사용하면 편하게 객체 상태를 저장할 수 있다.

   ※ RMI(Remote Method Invocation)
      서로 다른 가상 기계장치에 존재하는 함수를
	  호출하고 실행하는 기능을 담당한다.
	  서로 다른 통신 구조에 위치한 각각의 원격 객체들 간의
	  통신 구조를 지원하는 개념으로 이해하면 좋겠다.

	  
   ※ Bean(빈, 자바 빈)
      C/S(Client, Server) 구조적 모델에서 서버측 구조에 해당하며,
	  재사용 가능한 소프트웨어 개체를 만들 수 있게 하는 컴포넌트 기술.
	  작성된 개체의 공유가 가능하며 프로젝트에 쉽게 포함시킬 수 있도록 한다.
	  클라이언트에게 빈이라는 프로그램 컴포넌트를 분배하는 방식으로 처리.

----------------------------------------------------------
implements 하기만 하면
자바가 객체 직렬화가 필요한 클래스라는 사실을 알 수 있게 된다.
-------------↓-------------------------------------------
	
○ Serializable 인터페이스
 
   객체 직렬화를 하기 위해 먼저 객체 직렬화가 가능하도록
   java.io.Serializable 인터페이스를 구현해 주어야 한다.
   이 인터페이스는 객체 직렬화가 제공되어야 한다는 사실을
   JVM 에 알려주는 역할을 수행한다.
   또한, Serializable 인터페이스는
   다른 인터페이스와 달리 구현해야 할 메소드가 없기 때문에
   단지 선언만 해주면 된다.

   형식)
   ①
   public class 클래스명 implements Serializable
   {
	   ...
   }

   ②
   Serializable 인터페이스를 구현한 후
   ObjectInputStream 클래스와 ObjectOutputStream 클래스를 이용하여
   객체 단위로 입출력을 수행하게 된다.

   ※ 멤버 변수가 static 으로 선언된 경우(즉, 클래스 변수일 경우)
      객체 직렬화의 대상에서 제외된다.

○ Object Stream [객체 직렬화 해제 → Byte를 다시 객체로 변경...]

	[Byte 기반 스트림.]
   java.io.ObjectinputStream 클래스는 → (객체를 읽어들이는 Stream)
   java.io.ObjectOutputStream 클래스에 의해 → (객체를 내보내는 Stream)

   파일에 저장되어 있는 객체나 네트워크를 통해 전달된 객체의
   직렬화를 해제하는 기능을 제공한다.

   단, java.io.Serializable 인터페이스와
   java.io.Serializable 인터페이스를 지원해주는 객체에 대해서만
   사용이 가능하다.

 
                                 ↓(*영속성을 갖는)
   즉, Serializable 인터페이스와 Externalizable 인터페이스를
                                  ̄ ̄ ̄ ̄ ̄ ̄ ̄
   구현한 객체에서만 사용이 가능하다는 것이다.
   이 때, readObject() 메소드를 이용하여
   스트림으로부터 직렬화된 객체를 읽을 수 있으며
   이렇게 읽은 객체는 배열, 문자열 또는 각 객체 등   ▶ 다운 캐스팅 
   원래의 형(Type)으로 캐스팅 해 주어야 한다.        (Object → 원래의 형(String, arr...)

	=======================================================
   직렬화 되어 있거나, 영속성을 갖도록 구성되어 있지 않다면
   ObjectStream 사용할 수 없다.
   ========================================================


*/




// [개인 필기]====================================================================

/*

	[RMI] (PDA 사용을 대비하여 만들어진 자바의 기술이다.)------------------
	다른 실행 환경에 있는 객체의 메소드를
	로컬에서 생성한 객체의 메소드와 다름 없이
	호출할 수 있도록 하는 자바의 분산 객체 기술
	-----------------------------------------------------------------------
	exit(-1) → RMI를 염두에 두고 만들어진 메소드(매개변수 -1)
	A와 B 디바이스 사이에서
	제대로 종료됐음을 확인하기 위해서 return 값 -1 을 만들어 낸 것이다.
	A가 종료를 명령했을 때, B 디바이스에서 제대로 종료되었다는 의미로 -1 값을
	반환한다.
	-----------------------------------------------------------------------
	[Bean]
	속성만 존재하는 클래스를 Bean이라고 한다.
	-----------------------------------------------------------------------
	
	
	★컴포넌트 : 여러 개의 프로그램 함수들을 모아
	             하나의 특정한 기능을 수행할 수 있도록 구성한 작은 기능적 단위

	게임의 팩 하나하나가 컴포넌트...
	게임팩이 지켜야 하는 것들을 러프하게 만들어서(인터페이스와 비슷)
	어떤 게임이 됐든 간에 그 규격만 만족하면 게임기에서 동작할 수 있도록.
	→ Java bean
	기본 가이드라인 구현 → 부분적으로 하나씩 구현하여 
	                        기능들을 끼워 넣을 수 있게된다.

*/




// 멤버변수만이 객체 직렬화의 대상이다. (사과의 개수, 가진 돈 ...)


// 메모리에 있는 객체를 파일로 변환해서 전달하기 위해 거치는 과정을
// 객체 직렬화 라고 한다.

// 클래스 저장이 객체 저장인가? Ⅹ! → 설계도 저장이다.

// 사과 장수라는 객체(인스턴스가 생성된 객체) → Memory 

// 네트워크 통해서 전송, 파일로 저장하려 할 때 어떻게 처리해야 하나?
// → 1과 0의 형태로 쪼개져서 저장.



// 직렬화 : Serialization ▶ 잘게 쪼개진 데이터에 각각에 번호를 붙이는 것이다.
// (직렬 병렬의 그 직렬이 아니라, 잘게 쪼갠 데이터에 번호를 붙인다는 의미이다.)


// 쪼개진 각각의 조각에 Serial Number(이름과 같은 용도의 번호)를 붙인다면
// 나중에 붙이는 과정에서 더욱 용이하게 객체를 복원할 수 있을 것이다.

// 전달하려는 데이터를 잘게 쪼개서 캡슐화하는 과정을 거쳐야 제대로 전달된다...
// ===============================================================================

// getProperty() → 속성(인코딩 방식)을 가져올 때!
// 아! 인코딩 방식 읽어올 때 사용했었다!!!!!!!!
// System.out.println("디폴트 캐릭터셋 : " + System.getProperty("file.encoding"));


import java.util.Hashtable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;


import java.io.FileInputStream;
import java.io.ObjectInputStream;
// import java.util.Hashtable;
import java.util.Enumeration;




public class Test177
{
	public static void main(String[] args) throws Exception // lang package에 있으므로 임포트 x
	{
		// application Directory 를 가져오기 위해
		// 사용자의 Directory 속성을 가져온다.

		// 시스템 속성으로부터 현재 사용자가 사용중인 디렉터리 정보 얻어오기
		String appDir = System.getProperty("user.dir");

		// 테스트(확인)
		System.out.println(appDir);
		//--==>> C:\JavaStudy
		
		// Test177이 JavaStudy 디렉터리에서 작성되고 있음을 알려주는 것이다.

		// 파일 객체 생성
		File f0 = new File(appDir, "\\data\\test.ser");
		//-- appDir → C:\JavaStudy
		//-- appDir 위치를 기준으로(중심으로) "\\data\\test.ser"를 구성하겠다는 의미
		//-- 결과적으로... 『C:\JavaStudy\data\test.ser』 구성.

		
		// 부모 디렉터리에 접근접근해서 → f0이 존재하는지 확인
		// C:\JavaStudy\data\ 이 경로가 존재하니? → false 보내줌...없어!

		// 테스트(확인)----------------------------------------------------
		//System.out.println(f0.getParentFile().exists());
		//--==>> false
		//--> 『test.ser』 파일이 만들어지게 될 디렉터리 경로가 구성되어 있지 않다.
		// ----------------------------------------------------------------
		// ※ 『C:\JavaStudy』 경로에 『data』 디렉터리 생성~!!!

		
		// 생성 이후 다시 테스트(확인)------------------------------------------
		//System.out.println(f0.getParentFile().exists());
		//--==>> true
		//--> 『test.ser』 파일이 만들어지게 될 디렉터리 경로가 구성되어 있다.
		// ---------------------------------------------------------------------

		
		// 『test.ser』 파일이 만들어지게 될 디렉터리 경로가 구성되어 있지 않다면...
		if (!f0.getParentFile().exists()) // data 디렉터리 만들어지지 않았다면....
		{
			// 디렉터리를 만들겠다. (생성하겠다.)
			f0.getParentFile().mkdirs();	// → Make Directories!
		}

		// Hashtable 자료구조 인스턴스 생성
		Hashtable<String, String> h1 = new Hashtable<String, String>();

		// 생성한 h1 이라는 Hashtable 자료구조에 요소 추가
		//       key       value
		h1.put("2308112", "노은하");
		h1.put("2308103", "문정환");
		h1.put("2308115", "박가영");
		h1.put("2308107", "박나영");
		h1.put("2308136", "박범구");

		// 테스트(확인)
		//System.out.println(h1.get("2308107"));
		//--==>> 박나영

		// (byte stream)
		// [FileOutputStream]
		// → 파일로부터 바이트를 입력받아 바이트 단위로 출력할 수 있게 해주는 클래스.

		// 파일 전용 출력 스트림 생성 (수도꼭지 열기)
		FileOutputStream fos = new FileOutputStream(f0);
		//-- 파일 전용 출력 스트림(물줄기)에
		//   f0 라는 파일 객체를 띄우겠다.
		// InputStreamReader isr = new InputStreamReader(System.in);

		// 객체 전용 출력 스트림 생성
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		//-- 객체 전용 출력 스트림(물줄기)에
		//   fos 라는 파일 전용 출력 스트림을 감싸겠다.
		// BufferedReader br = new BufferedReader(isr);
		
		
		// 위의 line 254 ~ 260 과 동일한 구문
		// ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f0));
		// ===================          ==================     --------------------
		//      오브젝트                 파일 → 오브젝트?             파일

		
		// 위의 line 267 의 개념과 비교할 구문(구조적으로 동일한 구문)
		// // BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		// ※ ObjectOutputStream 클래스는
		//    객체들을 출력하는 기능을 제공하는 클래스로
		//    출력 스트림에 출력하기 전에
		//    내부적으로 객체 직렬화를 수행하게 된다.
		//    자바 기본 데이터 또는 객체들을
		//    파일에 저장하거나 네트워크를 통해 전달하기 위해
		//    전달할 객체를 직렬화 하는 기능을 제공하는 것이다.

		
		// Stream에 h1을 기록하겠다.
		// 생성된 스트림에 내보낼 객체를 기록.
		oos.writeObject(h1);
		//-- out.write(ch); 와 같은 개념의 구문

		
		// 객체 전용 리소스 반납 (겉을 감싼 것이 ObjectOutputStream 이므로 oos 먼저 close())
		oos.close();
		//-- ObjectOutputStream 리소스 반납


		// 파일 전용 리소스 반납
		fos.close();
		//-- FileOutputStream 리소스 반납.

		// (객체를 직렬화하여 파일로) 내보내기 끝~!!!
		//------------------------------------------------------------------------

		// (객체를 직렬화하여 내보낸 파일) 읽어들이기 시작~!!!

		// f0 파일 객체가 존재한다면...
		if (f0.exists())
		{
			// f0 파일을 파일 입력 스트림(fis, FileInputStream)으로 읽어들이고
			FileInputStream fis = new FileInputStream(f0);

			// fis 파일 입력스트림으로부터 객체 입력 스트림(ois, ObjectInputStream)을 얻어내어
			ObjectInputStream ois = new ObjectInputStream(fis);

			// 객체 입력 스트림(ois, ObjectInputStream)으로부터 읽어들인 객체(Object)를
			// 캐스팅(Hashtable) 하여 h2 라는 자료구조에 담아내기
			//Object obj = ois.readObject();		    //-- 오브젝트 타입을 얻어낸다.
			Hashtable h2 = (Hashtable)ois.readObject(); // 원래 해시테이블을 담았던 데이터 이므로(다운캐스팅)

			ois.close();
			//-- ois, ObjectInputStream 리소스 반납

			fis.close();
			//-- fis, FileInputStream 리소스 반납
			
			//-----------------------여기까지 수행하면 읽어들이는 작업 끝~!!!
				
			// 읽어들인 h2 객체의 내용 확인

			String key;
			String value;
			

			// Hashtable 자료구조에서는 Iterator 사용 불가

			// ※ Iterator 사용할 수 없음
							// 모든 key를 반환한다.
			Enumeration e = h2.keys();
			

			while (e.hasMoreElements())
			{
				key = (String)e.nextElement();
				//-- Hashtable 자료구조를 대상으로 key 를 읽어들이는 과정.

				// 테스트(확인)
				//System.out.println(key);
				//--==>> 2308136
				//       2308115
				//       2308107
				//       2308112
				//       2308103


				value = (String)h2.get(key);
				//-- Hashtable 자료구조를 대상으로 key 를 활용하여 value 를 얻어내는 과정

				System.out.println(key + " → " + value);
				//--==>> 2308136 → 박범구
				//       2308115 → 박가영
				//       2308107 → 박나영
				//       2308112 → 노은하
				//       2308103 → 문정환

			}

		}
		
	}
}