Web/Java

[Java] 직렬화

hikr90 2025. 1. 9. 21:54

# 직렬화 (Serializable)

- 객체 또는 데이터를 외부에서도 사용할 수 있도록 바이트의 형태로 변환하는 작업을 뜻한다.

- 반대로 바이트로 변환된 데이터를 다시 객체로 변환하는 작업을 역직렬화라고 한다.

- 모든 클래스가 직렬화하는 것은 불가능하고 Serializable 인터페이스를 구현하는 클래스만 직렬화가 가능하다.

 

# serialVersionUID

- 직렬화의 고유 버전

- 직렬화, 역직렬화를 진행할 때 이 값으로 클래스의 특정 버전이 맞는지를 판단한다.

- 값을 선언하지 않아도 default값을 생성하므로 사용에는 문제가 없지만 컴파일러에따라서 변경이 될 수 있으므로 작성을 권장한다.

 

public class 클래스명 implements Serializable {
     private static final long serialVersionUID = 1L;
}

 

[참고] 자바에서의 직렬화는 Serializable  인터페이스를 클래스에서 구현하는 방식으로 사용한다.


 

# RspInfo.java

package ex1_object_stream;

import java.io.Serializable;

public class RspInfo implements Serializable{
	
	/*	직렬화 (Serializable)
	 * 	- 객체 또는 데이터를 외부에서도 사용할 수 있도록 바이트 형태로 변환하는 작업
	 * 	- 인터페이스이므로, 클래스 implements Serializable 방식으로 구현한다.

	 * 	- 반대로 바이트로 변환된 데이터를 다시 객체로 변환하는 작업을 역직렬화라고 부른다.
	 */
	
	
	// - 이 클래스는 유저의 아이디와 전적을 관리한다.
	// - Object Stream 을 사용하여 객체를 통으로 읽고 쓰므로 객체의 직렬화가 필수이다.
	
	/*	java.io.NotSerializableException
	 * 	- 직렬화할 수 없다. 라는 오류
	 * 	- heap 영역 안에 RspInfo 클래스 내에 있는 각각의 변수 주소 값이 다 흩어져있어서 생긴 오류이다.
	 * 	- Object Stream에서는 클래스의 id, win, lose, draw 변수를 기억하고자하지만 각 주소 값이 다르다보니 어떤 값을 참조해야하는지 알 수 없다.
	 * 
	 * 	- 그래서, 하나의 배열 형태를 만들어서 그 곳에 승무패의 정보를 저장한 뒤
	 * 	- 그 배열의 주소 값을 Object Stream 에 전달해야한다.
	 * 
	 * 	- 위의 작업이 직렬화이다. 이 작업이 없으면 객체를 직렬화 할 수 없다.
	 */
	
	// 유저의 아이디를 저장할 변수
	private String id;
	
	// 승무패를 저장할 변수
	private int win, lose, draw;

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public int getWin() {
		return win;
	}

	public void setWin(int win) {
		this.win = win;
	}

	public int getLose() {
		return lose;
	}

	public void setLose(int lose) {
		this.lose = lose;
	}

	public int getDraw() {
		return draw;
	}

	public void setDraw(int draw) {
		this.draw = draw;
	}
}

 

# RspMain.java

package ex1_object_stream;

import java.util.Random;
import java.util.Scanner;

public class RspMain {
	public static void main(String[] args) {
		//
		RspInfo info = new RspInfo();
		Scanner sc = new Scanner(System.in);
		String id = "";
		int win = 0;
		int lose = 0;
		int draw = 0;
		//
		System.out.println("id : ");
		id = sc.next();
		info.setId(id);
		//
		try {
			// 정보를 가져오는 클래스를 선언
			ScoreLoader s1 = new ScoreLoader(id);
			// 전적 변수로 저장
			win = s1.getInfo().getWin();
			lose = s1.getInfo().getLose();
			draw = s1.getInfo().getDraw();
			// 전적 세팅
			info.setWin(win);
			info.setLose(lose);
			info.setDraw(draw);
		
		} catch (Exception e) {
			// TODO: handle exception
		}
		
		// 전적 확인
		System.out.printf("%d승 %d패 %d무\n",win,lose,draw);
		//
		while(true) {
			//
			int random = new Random().nextInt(2);
			//
			System.out.println("가위(s) | 바위(r) | 보(p) : ");
			String user = sc.next();
			int usercnt = 0;
			//
			switch (user) {
				case "s":
					usercnt = 0;
					break;
				case "r":
					usercnt = 1;
					break;
				case "p":
					usercnt = 2;
					break;
			} // switch
			
			// 경우의 수를 판단
			if(usercnt-random==-2||usercnt-random==1) {
				// 선행으로 집어넣는다.
				info.setWin(++win);
				System.out.println("이겼습니다!");
			} else if(usercnt-random==0) {
				info.setDraw(++draw);
				System.out.println("비겼습니다.");
			} else {
				info.setLose(++lose);
				System.out.println("졌습니다.");
			}
			//
			System.out.printf("%d승 %d패 %d무%n",info.getWin(),info.getLose(),info.getDraw());
			
			// 더할건지 질문
			System.out.print("한판 더? y / n : ");
			String exit = sc.next();
			//
			if(!exit.equalsIgnoreCase("y")) {
				System.out.println("게임을 종료합니다.");
				break;
			} else {
				
			}
		} // while
		
		// Object Stream 동작
		// - 게임이 종료되었으므로 객체를 저장한다.
		// - 저장은 한번만해주면 되니까 익명클래스로 선언
		new ScoreWriter(info);
	} //main
}

 

# ScoreLoader.java

package ex1_object_stream;

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

public class ScoreLoader {
	// 정보 클래스
	private RspInfo info;
	
	// 정보를 가져오는 메소드
	public RspInfo getInfo() {
		return info;
	}
	
	// 생성자
	public ScoreLoader(String id) {
		String path = "C://JAVA1_0713_KTH/Game/"+id+"/Gamesav.sav";
		File f = new File(path);
		//
		if(f.exists()) {
			// 처음이 아닌 사람인 경우
			FileInputStream fis = null;
			ObjectInputStream ois = null;
			
			// 파일 로드
			try {
				//
				fis = new FileInputStream(f);
				ois = new ObjectInputStream(fis);
				
				// readObject 메소드로 객체 자체를 받아온다.
				// Object가 더 큰 객체이므로 큰 객체를 작은 객체에 넣으니까 형변환을 해줘야한다.
				info = (RspInfo)ois.readObject();	// New가 없어도, 주소 값을 받으니 heap 영역이 있다.
				System.out.println("로드 완료");
				
			} catch (Exception e) {
				e.printStackTrace();
				System.out.println("로드 실패");
			} finally {
				// 메인에서 다시 스로우 하지 않도록 try 처리
				try {
					ois.close();
					fis.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
		} else {
			// 첫 시작인 사람
			System.out.println("아이디 생성을 환영합니다.");
		}
	}
}

 

# ScoreWriter.java 

package ex1_object_stream;

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

public class ScoreWriter {
	public ScoreWriter(RspInfo info) {
		//
		String path = "C://JAVA1_0713_KTH/Game/"+info.getId()+"/Gamesav.sav";
		
		// 전적 정보를 저장한다.
		File dir1 = new File("C://JAVA1_0713_KTH/Game/");
		//
		if(!dir1.exists()) {
			// 폴더가 없으면 생성한다.
			dir1.mkdirs();
		}

		// 분류 폴더는 생성되었으니, 사용자별 폴더가 있는지 확인
		File dir2 = new File(dir1,info.getId());
		if(!dir2.exists()) {
			// 없으면 생성
			dir2.mkdirs();
		}
		
		/*	ObjectOutputStream
		 *		- 클래스를 통으로 저장할 수 있게해주는 보조적인 스트림
		 *		- 클래스를 입출력하는 경우 반드시 바이트 기반으로 진행해야한다. 
		 *		- 전송하는 객체 파일은 반드시 직렬화 인터페이스를 구현해야한다.
		 */
		
		// RspInfo로 아이디, 명칭, 전적까지 전부 받았다.
		FileOutputStream fos = null;
		ObjectOutputStream oos = null;
		//
		try {
			// 파일 쓰기
			fos = new FileOutputStream(path);
			oos = new ObjectOutputStream(fos);
			
			// 객체를 저장하는 경우 writeObject를 사용한다.
			oos.writeObject(info);	// 아이디 폴더 안에 Gamesave.sav 로 저장한다.
					
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("기록저장 실패");
		} finally {
			// 메인에서 throws를 사용하지 않도록 try - catch 사용
			try {
				oos.close();
				fos.close();
			} catch (IOException e) {
				
			}
		}
	} // constructor
}

 

'Web > Java' 카테고리의 다른 글

[Java] 입출력  (0) 2025.01.09
[Java] 스레드  (0) 2025.01.09
[Java] 에러  (0) 2025.01.09
[Java] 인터페이스  (0) 2025.01.09
[Java] 상속  (0) 2025.01.09