본문 바로가기
JAVA/JAVA Note

[JAVA Note] 22. 직렬화

by objade 2022. 11. 16.

 

1. Serialize
: 파일에 데이터를 기록하기 위해서는 파일에 기록할 모든 요소가 다 직렬화가 되어있어야 함

1) 리스트의 객체를 파일로 저장해보기

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Arrays;

public class Ex01 {
	public static void main(String[] args) throws IOException {
		ArrayList<String> list = new ArrayList<String>(		
			Arrays.asList(new String[] {	
				"이지은", "홍진호", "나단비"	
			})
		);

		FileOutputStream fos = new FileOutputStream("ex01.dat");
		ObjectOutputStream oos = new ObjectOutputStream(fos);
				
		oos.writeObject(list);		
				
//		oos.write(buf);
		oos.write("빼빼로".getBytes());
				
		oos.flush();
		oos.close();
	
		System.out.println(list);
		
	}
}


- 배열 - list - ArrayList 의 순서대로 문자열이 저장됨
- asList는 static이기 때문에, 객체를 따로 생성하지 않아도 사용할 수 있음
- writeObject() : implement Serializable 한 객체를 byte 단위로 분해하여 저장
→ ArrayList는 Serializable이 implement 되어있기 때문에 writeObject 사용 가능


- 확인되지 않은 클래스 다운캐스팅에 대하여 경고하지 않는다.

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;

public class Ex02 {
	
	@SuppressWarnings("unchecked")		
	
	public static void main(String[] args) throws IOException, ClassNotFoundException {
		
		FileInputStream fis = new FileInputStream("ex01.dat");
		ObjectInputStream ois = new ObjectInputStream(fis);
		
		ArrayList<String> list = (ArrayList<String>) ois.readObject();
		
		System.out.println("불러온 리스트 : " + list);
		
//		String test = (String)ois.readObject();
//		System.out.println(test);
		
		ois.close();
	}
}


* json 형식의 파일은 DB에 바로 넣을 수 있음
→ 직렬화의 필요성이 낮아짐

2) 객체 파일 저장
* 파일 입출력 클래스 
- InputStream : 입력 스트림
- OutputStream : 출력 스트림
→ 어디에서 무엇을 불러올 것인지 생각할 것.

- InputStreamReader : 문자 입력 스트림
- OutputStreamWriter : 문자 출력 스트림

- ObjectInputStream : 객체 입력 스트림
- ObjectOutputStream : 객체 출력 스트림

- FileInputStream : 파일에서 불러오는 스트림
- FileOutputStream : 파일에서 내보내는 스트림

- FileReader : FileInputStream + InputStreamReader
- FileWriter : FileOutputStream + OutputStreamWriter

- Scanner : File or String InputSteam
→ Reader에 속함.

~ Todo 클래스 만들기

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

class Todo implements Comparable<Todo>, Serializable{	
	private static final long serialVersionUID = 6737531346498093713L;
	
	// 직렬화 하여 전송 이후 재조립할 때 (역직렬화) 원래 자료형이 맞는지 확인하기 위한 식별 값 
	
	private String title;
	private Date date;
	
	public Todo(String title, Date date) {
		this.title = title;
		this.date = date;
	}

	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public Date getDate() {
		return date;
	}
	public void setDate(Date date) {
		this.date = date;
	}

	@Override
	public String toString() {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd");
		return String.format("[%s]\t%s", title, sdf.format(date));
	}

	@Override
	public int compareTo(Todo o) {
		return this.date.compareTo(o.date);
	}		
}


① private 필드를 작성하고 저장함
② 저장 이후 우클릭 - s, o (필드를 매개변수로 전달받는 생성자 자동작성)
③ 저장 이후 우클릭 - s, r - alt + a, r (각 필드에 대한 getter/setter 자동작성)

- SerialVersionUID : 직렬화 하여 전송 이후 재조립할 때 (역직렬화) 원래 자료형이 맞는지 확인하기 위한 식별 값 

* Date는 Long형태의 정수형 데이터를 클래스로 만든 것과 같은 셈임
* 기본 생성자가 없기 때문에, java beans가 아님
* java beans : 데이터를 저장하는 용도로 사용할 수 있는 자바 클래스 형식

~ 메인 함수

public class Ex03 {
	public static void main(String[] args) throws ParseException, IOException {
		
		ArrayList<Todo> list = new ArrayList<Todo>();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd");
		
        list.add(new Todo("빼빼로데이", sdf.parse("2022.11.11")));
        list.add(new Todo("홍길동 생일", sdf.parse("2022.11.04")));
        list.add(new Todo("응용SW평가(필기)", sdf.parse("2022.11.15")));
        list.add(new Todo("나단비 생일", sdf.parse("2022.12.19")));
        list.add(new Todo("가나다 생일", sdf.parse("2022.12.22")));	

        list.sort(null);
        list.forEach(todo -> System.out.println(todo));

     //	FileOutputStream fos = new FileOutputStream("ex.03.dat");	
     //	ObjectOutputStream oos = new ObjectOutputStream(fos);

        // 위의 코드를 줄여서 한줄로 쓸 수도 있음 

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ex03.dat"));

        oos.writeObject(list);
        oos.flush();
        oos.close();

        System.out.println("저장완료");
    }
}



- ex03.dat의 객체를 list에 저장하여 아래 출력코드가 작동할 수 있도록 처리해보기

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;

public class Ex04 {
	
	@SuppressWarnings("unchecked")
	
	public static void main(String[] args) throws IOException, ClassNotFoundException {
		
		ArrayList<Todo> list = null;
		
//		FileInputStream fis = new FileInputStream("ex03.dat");	
//		ObjectInputStream ois = new ObjectInputStream(fis);
		
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ex03.dat"));
	
		list = (ArrayList<Todo>) ois.readObject();


- ois.readObject()는 Object로 업캐스팅 되어있는 상태이기 때문에, list에 저장하기 위해서는 ArrayList<Todo> 로 다운캐스팅 해야함

- 출력문
① index를 활용한 for문

for(int i = 0; i < list.size(); i++) {
	Todo t = list.get(i);
	System.out.println(t);
}

System.out.println("\n==============================================\n");


② 향상된 for문

for(Todo t : list) {	
	System.out.println(t);
}

System.out.println("\n==============================================\n");


- 여기까지는 array, list 모두 사용 가능
→ 반복문을 씀

③ 람다식과 forEach문을 활용
- list에서만 됨   → 반복문을 쓰지 않음

list.forEach(t -> System.out.println(t));

System.out.println("\n==============================================\n");



④ forEach문
- list에서만 됨   → 반복문을 쓰지 않음

list.forEach(System.out::println);	// forEach문	

System.out.println("\n==============================================\n");


ois.close();	// 열린 stream을 닫아줌


- Collection은 주로 함수(메서드)를 이용해서 작업을 함
→ 반복문을 이용한 작업은 권장하지 않음


~ 위에서 만든 ex03.dat 파일을 사용하여 할 일 목록 관리 프로그램 만들기
- Handler
① Handler의 생성자
→ load() 메서드를 호출함

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;

public class Handler {
	
	private ArrayList<Todo> list;
	private File f = new File("ex03.dat");
	
	public Handler() {
		load();
	}


② load() : 생성자 내부에서 호출함. 파일에서 객체를 불러와서 리스트에 저장

@SuppressWarnings({ "unchecked" })
	
public void load() {		
ObjectInputStream ois = null;
	try {
		ois = new ObjectInputStream(new FileInputStream(f));
		list = (ArrayList<Todo>) ois.readObject();
	} catch (ClassNotFoundException e) {
		e.printStackTrace();
	
	} catch (IOException e) {
		e.printStackTrace();
	} finally {
		try {
			ois.close();
		} catch (IOException e) {}
	}


- 메서드를 호출하여 쓸 경우에는, 가급적이면 try ~ catch를 사용해서 예외처리를 해주는 것이 좋음
- throw로 넘긴다면, 호출한 메서드에서도 계속 throw로 넘겨야하기 때문임

② Save() : main에서 호출함. 리스트 객체를 파일로 저장

public void save() throws IOException {		
	//	FileWriter fw = new FileWriter(f);
		
	ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f));
	oos.writeObject(list);
			
	oos.flush();
	oos.close();
	System.out.println("저장완료");
		
}


③ insert() : 객체를 전달 받아서 추가하기

public int insert(Todo todo) {	
	
	list.add(todo);
	
	return 1;
}


④ delete() : 선택한 객체의 index를 확인하여 제거하기

public int delete(String find) {	
		
	list.removeIf(td -> td.getTitle().equals(find));
		
	return 1;		
}


⑤ showList() : 객체를 출력함

public void showList() {
		
	for(Todo td : list) {
		System.out.println(td);
	}
	System.out.println();
		
}


⑥ sortList() : 리스트를 날짜순으로 정렬함

public void sortList() {
	list.sort((a, b) -> a.getDate().compareTo(b.getDate()));
}


- Main

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Scanner;

public class Main {
	public static void main(String[] args) throws IOException, ParseException, ClassNotFoundException {
		
		Handler hd = new Handler();
		
		Scanner sc = new Scanner(System.in);
		int select, row;
		String find;
		
		Todo td = null;

            do {
                System.out.println("1. 추가 | 2. 목록보기 | 3. 삭제 | 4. 저장 | 0. 종료");
                System.out.print("선택 : ");
                select = Integer.parseInt(sc.nextLine());

                switch(select) {
                case 1 :
                    td = makeTodo(sc);
                    row = hd.insert(td);
                    System.out.println(row == 1 ? "추가 성공" : "추가 실패");
                    break;

                case 2 :
                    hd.sortList();
                    hd.showList();
                    break;

                case 3 :
                    System.out.print("삭제할 일정 이름 입력 : ");
                    find = sc.nextLine();

                    row = hd.delete(find);
                    System.out.println(row == 1 ? "삭제 성공" : "삭제 실패");

                    break;

                case 4 :			
                    hd.save();		
                    break;	
                }

            } while(select != 0);
                System.out.println("프로그램 종료");
                sc.close();		
            }

    private static Todo makeTodo(Scanner sc) throws ParseException {

        String todo;
        String date;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd");

        System.out.print("할 일 입력 : ");  todo = sc.nextLine();
        System.out.print("날짜 입력 (yyyy.MM.dd): ");  date = sc.nextLine();

        Todo td = new Todo(todo, sdf.parse(date));

        return td;
        }
}

목록에서 삭제
목록에 추가
저장 및 프로그램 종료


- Serialize를 한 객체는 파일 입출력시 데이터를 바이트 단위로 끊어서 사용함
~ ex01.dat 파일의 복사본을 만들고, 버퍼의 크기를 구하기

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Ex05 {
    public static void main(String[] args) throws IOException {

        FileInputStream fis = new FileInputStream("ex01.dat");
        FileOutputStream fos = new FileOutputStream("ex01.copy");

        byte[] buffer = new byte[512];
        int readBytes;
        
        while((readBytes = fis.read(buffer)) != -1) {

        fos.write(buffer, 0, readBytes);

        System.out.printf("=== %d byte ===\n", readBytes);

        String str = "";

        for(int i = 0; i < buffer.length; i++) {
            System.out.printf("%02X ", buffer[i]);
            str += (char)buffer[i];

            if((i + 1) % 8 == 0) {
                System.out.println("\t" + str);
                str = "";
                System.out.println();
                }
            }
            System.out.println();
        }
		
        fis.close();
        fos.close();

        }
}


~ 웹 상의 이미지 주소를 작성하여 이미지 다운로드 해보기

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;


public class Ex06 {
	public static void main(String[] args) throws IOException {
		String path = "https://search.pstatic.net/common/?src=http%3A%2F%2Fblogfiles.naver.net%2FMjAyMjAxMThfMjk2%2FMDAxNjQyNTE1MTg1MDA2.YLtsu24UH52R8WlGMdPjkXqcBPletaNNuyzxw2Ua5Wgg.MP6AUYe6YPt4Lz_MqiTwO4iC6VWI4PJHhZE3W5SaA6cg.JPEG.black_ssol%2F20181020%25A3%25DF142622.jpg&type=sc960_832";
		URL url = new URL(path);
		
		InputStream fis = url.openStream();
		FileOutputStream fos = new FileOutputStream("바다.jpg");


+ 파일의 크기 구하기

	byte[] buffer = new byte[1024];
	int readBytes;
	int allCount = 0;
		
	while((readBytes = fis.read(buffer)) != -1) {
			
		fos.write(buffer, 0, readBytes);
		allCount += readBytes;
		System.out.printf("=== %d byte ===\n", allCount);
	}
			
	fis.close();
	fos.close();
	
	}
}

 


+ 연결된 자원의 컨텐트 유형을 문자열 형태로 확인할 수 있도록 함

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;


public class Ex07 {
	public static void main(String[] args) throws IOException {
		
        String path = "https://search.pstatic.net/common/?src=http%3A%2F%2Fblogfiles.naver.net%2FMjAyMjEwMTdfMjk2%2FMDAxNjY1OTM0MTM2ODg4.pHDlZDhaWc3VbJ9dFps-58oSBUQPJfOijB_oM2vX9zcg.IHWAbBCU9f8Bs6w5HGRVu8IHEm-9aAF5Y5EJZOMDXSEg.JPEG.tjdduq33%2Fphoto-1532357716190-db6e6a7db9fd.jpg&type=sc960_832";
        URL url = new URL(path);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        String contentType = conn.getContentType();	
        // 연결된 자원의 컨텐트 유형을 문자열 형태로 확인할 수 있다.

        Scanner sc = new Scanner(System.in);
        String fileName;

        System.out.println("컨텐트 유형은 " + contentType + "입니다.");
        System.out.print("원하는 파일 이름을 입력하세요 : "); 	
        // 파일이름.컨텐트 타입	(ex - 은하수.jpg)
        fileName = sc.nextLine();

        InputStream fis = url.openStream();
        FileOutputStream fos = new FileOutputStream(fileName);

        // 파일의 크기 구하기

        byte[] buffer = new byte[1024];
        int readBytes;
        int allCount = 0;

        while((readBytes = fis.read(buffer)) != -1) {
            fos.write(buffer, 0, readBytes);
            allCount += readBytes;
            System.out.printf("=== %d byte ===\n", allCount);
            }

        fis.close();
        fos.close();

        sc.close();
	}
}




'JAVA > JAVA Note' 카테고리의 다른 글

[JAVA Note] 21. 파일 입출력  (1) 2022.11.16
[JAVA Note] 20. 예외  (0) 2022.11.16
[JAVA Note] 19. Collection  (1) 2022.11.16
[JAVA Note] 18. Object  (0) 2022.11.06
[JAVA Note] 17. 추상화  (0) 2022.11.06

댓글