본문 바로가기

카테고리 없음

[ Java ] 객체의 직렬화(Serialization)

지금 내 운영체제(윈도우즈)에서 자바(언어로 만든) 프로그램이 동작하면서 데이터를 생성한다(→ 예를 들면 오늘의 할 일을 작성하거나 일기를 쓰거나 등의 행위를 해서 데이터가 만들어지면 이 데이터는 지금 자바 프로그램이 실행되는 동안은 존재하지만 프로그램을 껐다가 다시 실행하면 그 이전의 행위로 인해 발생된 데이터는 존재하지 않는다)

 

자바 프로그램을 재시작하면 그 이전에 만들어진 데이터는 존재하지 않기 때문에 처음부터 다시 데이터를 만들어야 하는 번거로움이 있다. 이런 번거로움을 방지하기 위해 자바 프로그램이 실행되는 동안 만들어진 데이터를 외부 경로에 위치하는 파일에 기록하거나 네트워크를 통해 전송해서 저장하게 되고, 다시 자바 프로그램을 실행할 때 외부 경로에 위치하는 파일에 기록된 데이터를 읽어들이거나 네트워를 통해 저장되어있는 데이터를 요청하여 응답받게 되면 그 이전에 작업했던 데이터를 사용할 수 있게 된다.

 

자바 프로그램이 실행되는 동안에 생성된 데이터를 외부 경로의 파일에 기록(저장)하거나 네트워크를 통해 전송하여 저장하는 과정에서 데이터를(객체의 내용, 인스턴스의 변수에 저장된 값) I/O가 처리할 수 있는 형태로 변환해야하는데 이를 할 수 있게 해주는 것이 직렬화다.

 

직렬화는(Serialization) 자바 언어에서 사용되는 Object 또는 Data 를 다른 플랫폼의 다른 환경의(다양한 플랫폼에서 ex.리눅스, Mac OS) 자바 프로그램에서 사용할 수 있도록 바이트 형태의 연속적인 데이터로 변환하는 기술을 의미한다.

***serialize: 순번으로 늘어놓다, 번호순으로 나열하다; 연속극[시리즈]으로 방송하다, 연재하다.

 

- 자바 프로그램에서 생성된 객체 또는 데이터를 외부 경로의 파일에 저장하고,

외부 경로의 파일에 기록되어 있는 객체 또는 데이터를 자바 프로그램으로 읽어들이는 실습

package etc.api.io.obj;


import java.io.Serializable;

// 객체를(Snack 클래스로부터 생성된 인스턴스, 인스턴스가 저장하고 있는 변수의 값들을)
// 스트림으로 전송하려면 직렬화가 가능해야 한다.

// 직렬화란 자바 언어에서 사용되는 Object 또는 Data를 다른 환경의(다양한 플랫폼에서 ex.리눅스, Mac OS)
// 자바 프로그램에서 사용할 수 있도록 바이트 형태의 연속적인 데이터로 변환하는 기술을 의미한다.

// 바이트 형태의 연속적인 데이터로 변환하는 이유: 직렬화가 가능한 클래스로 선언해야 하는 이유:
// 자바 프로그램을 실행하면서 생성된 객체를 외부 경로의 파일에 저장[기록]하고,
// 다른 환경의 자바 프로그램에서 읽어들이면서 객체(데이터)가 저장하고 있는 값의 손상을 방지하기 위해
// 바이트 단위로 잘게 쪼개서 전송하고, 복원하는 것임.
public class Snack implements Serializable {
    private String snackName; // 과자이름
    private int year; // 출시연도
    private int price; // 가격
    private String taste; // 맛

    public Snack(String snackName, int year, int price, String taste) {
        this.snackName = snackName;
        this.year = year;
        this.price = price;
        this.taste = taste;
    }

    public String getSnackName() {
        return snackName;
    }

    public int getYear() {
        return year;
    }

    public int getPrice() {
        return price;
    }

    public String getTaste() {
        return taste;
    }

    public void setSnackName(String snackName) {
        this.snackName = snackName;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public void setTaste(String taste) {
        this.taste = taste;
    }

    @Override
    public String toString() {
        return "Snack{" +
                "snackName='" + snackName + '\'' +
                ", year=" + year +
                ", price=" + price +
                ", taste='" + taste + '\'' +
                '}';
    }
}

 

package etc.api.io.obj;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class SaveSnack {
    public static void main(String[] args) {

        List<Snack> snackList = new ArrayList<>();

        // 여러 개의 객체를 한 번에 추가하기 위해

        // Collections 인터페이스를 이용하는 방법
        /*
        Collections.addAll(snackList,
                new Snack("콘칩", 1999, 2000, "GOOD"),
                new Snack("사브레", 2000, 3000, "SOSO"),
                new Snack("오징어칩", 1990, 1500, "BAD")
        );
        snackList.forEach(System.out::println);
        */

        // 2. Arrays를 이용하는 방법
        snackList = Arrays.asList(
                new Snack("콘칩", 1999, 2000, "GOOD"),
                new Snack("사브레", 2000, 3000, "SOSO"),
                new Snack("오징어칩", 1990, 1500, "BAD")
        );

        // snackList.forEach(System.out::println);
        // System.out.println(snackList);

        try(FileOutputStream fos = new FileOutputStream("C://workspace//snack.sav")){
            // 자바에서 생성한 객체를 저장할 수 있는 보조 스트림을 사용해야 한다.
            ObjectOutputStream oos = new ObjectOutputStream(fos);

            oos.writeObject(snackList);

        }catch(Exception e){
            e.printStackTrace();
        }


    }
}

 

- 생성된 객체를 외부 경로의 파일에 저장하게 되면

바이트 단위로 쪼개진 객체의 내용들이 저장되는데 바이트 단위로 쪼개어졌기 때문에 글자들이 깨져있는 것을 볼 수 있다 ▼

 

```

ы sr java.util.Arrays$ArrayList命<앎?? [ at [Ljava/lang/Object;xpur [Letc.api.io.obj.Snack;=?Xt??  xp   sr etc.api.io.obj.Snacka샺?sP? I priceI yearL  snackNamet Ljava/lang/String;L tasteq ~ xp   ?   ? 肄섏묩t GOODsq ~   
?   ?  ?щ툕?늯 SOSOsq ~   ?   ?  ?ㅼ쭠?댁묩t BAD

 

```

 

- 외부 경로의 파일에 바이트 단위로 저장되어 있는 객체(의 정보)를 읽어보자

package etc.api.io.obj;

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.List;

public class LoadSnack {
    public static void main(String[] args) {
        try(FileInputStream fis = new FileInputStream("C://workspace//snack.sav")){
            // 객체를 불러올 보조스트림
            ObjectInputStream ois = new ObjectInputStream(fis);

            List<Snack> snackList = (List<Snack>) ois.readObject();
            
            /*
            for(Snack snack: snackList) {
                System.out.println("snack = " + snack);
            }
            */

            // System.out.println("snackList = " + snackList);

            // 읽어들인 List타입 참조변수가 가리키는 Snack객체들을 각각 출력하기
            snackList.forEach(System.out::println);
            

        }catch(Exception e){
            e.printStackTrace();

        }
    }
}