12_IO(입출력)
보조 스트림을 이용하면 파일 입출력을 객체 단위로 활용 가능함
=> 이는 컴퓨터에 기록할 용도이지 사람이 읽을 용도가 아니므로 파일을 열었을 때 글자가 깨지는 현상 자주 나올 수 있음!
ObjectDao클래스에 Phone 클래스의 객체를 넣어서 출력하고 싶다!
프로그램 ---> 외부 매체(파일)
출력
package com.kh.chap04.assist.part02.object.model.dao;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import com.kh.chap04.assist.part02.object.model.vo.Phone;
public class ObjectDao {
public void fileSave() {
// (기반) FileOutputStream: 파일에 데이터를 1byte 단위로 출력할 수 있는 기반 스트림
// +
// (보조) ObjectOutputStream : 객체 단위로 출력할 수 있는 보조 스트림
// => "1bye" 짜리 보조 "출력" 스트림에는 "1bye" 짜리 기반 "출력" 스트림을 써야 함
// 테스트용 객체 생성
Phone ph = new Phone("아이폰", 1300000);
// 0. 스트림 변수 선언 및 null로 초기화
ObjectOutputStream oos = null;
// 1. 스트림 객체 생성(== 연결 통로를 만들겠다)
try {
oos = new ObjectOutputStream(new FileOutputStream("phone.txt"));
// 2. 출력: ObjectOutputStram 객체에서 제공하는 writeObject() 메소드 사용
oos.writeObject(ph);
// 3. 자원 반납(== 연결통로를 끊겠다) => 반드시 finally 블럭 안에 작성
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 3. 자원 반납 (반드시)
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
직렬화 선언을 하지 않으면 객체를 파일로 내보낼 때 NotSerializableException이 발생
=> 1byte짜리 좁은 통로로 큰 객체가 지나갈 수 없기 때문에 가로로 배열해 주는 과정: 직렬화
Phone클래스에 직렬화 선언해 주고 import만 해 주면 오류 해결 됨!
import java.io.Serializable;
public class Phone implements Serializable { // 직렬화 선언
저장 후 재실행 해 보면
여전히 문자가 깨어진 듯하긴 하지만! 잘 기록이 된 상태임
=> 해당 파일은 사람이 읽기 위한 파일이 아닌 프로그램상으로 필요한 데이터를 사용할 때 두고두고 기록할 용도이기 때문에 깨지는 건 큰 상관 없음
=> 하지만 여전히 의심스럽다면ㅡㅡ? 입력을 해 보면 됨!
프로그램 <--- 외부 매체(파일)
입력
public void fileRead() {
// FileInputStream: 파일로부터 데이터를 1byte 단위로 읽어들이기 위해 사용되는 기반 스트림
// +
// ObjectInputStream: 객체 단위로 입력받기 위해 사용되는 보조 스트림
// 0. 스트림 변수 선언 및 null로 초기화
ObjectInputStream ois = null;
// 1. 스트림 객체 생성(== 연결 통로를 만들겠음)
try {
ois = new ObjectInputStream(new FileInputStream("phone.txt"));
// 2. 입력: ObjectInputStream 객체에서 제공하는 readObject() 메소드 사용
Phone ph = (Phone)ois.readObject(); // 다형성에 의해 DownCasting
System.out.println(ph);
// 3. 연결 통로 끊기(반드시) => finally 블럭에
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} finally {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 출력물
name: 아이폰, price: 1300000
=> 입력이 잘됨!
코드를 더 줄이고 싶다면?
try ~ with ~ resource 구문 버전
try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream("phone.txt"))) {
Phone ph = (Phone)ois.readObject();
System.out.println(ph);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
배열도 가능하지 않을까?
프로그램 ---> 외부매체(파일)
출력
public void fileSave() {
// FileOutputStram + ObjectOutputStream (1byte짜리 좁은 통로)
// 테스트용 객체 배열
Phone[] arr = new Phone[3]; // [0] [1] [2]
// 테스트용 데이터 담기
arr[0] = new Phone("아이폰", 1300000);
arr[1] = new Phone("갤럭시", 1500000);
arr[2] = new Phone("플립폰", 2000000);
// try ~ with ~ resource 구문으로 한 큐에 생성
try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("phones.txt"))){
// 출력: ObjectOutputStream 객체에서 제공하는 writeObject() 메소드 사용
// oos.writeObject(arr[0]);
// oos.writeObject(arr[1]);
// oos.writeObject(arr[2]);
// 반복문 이용해서 내보내기
for(int i =0; i < arr.length; i++) {
oos.writeObject(arr[i]);
System.out.println(arr[i]);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
// 출력물
name: 아이폰, price: 1300000
name: 갤럭시, price: 1500000
name: 플립폰, price: 2000000
프로그램 <--- 외부매체(파일)
입력
public void fileRead() {
// FileInputStream + ObjectInputStream
Phone[] ph = new Phone[3];
try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream("phones.txt"))) {
// toString() 메소드를 오버라이딩 했기 때문에 굳이 형변환 하지 않고 바로 출력해 보기
// System.out.println(ois.readObject() /* .toString() */);
// System.out.println(ois.readObject());
// System.out.println(ois.readObject());
// EOFExeption(End of file): 파일 내용이 끝났음에도 자꾸 출력하고자 할 때 발생하는 오류
// => IOException의 자식임
// System.out.println(ois.readObject());
// EOFException이 발생할 때까지만 반복을 돌리기
// 예측 불가능한 오류이기 때문에 정확한 조건을 세울 수 없음
while (true) { // 그래서 일단은 무한 반복
System.out.println(ois.readObject());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (EOFException e) { // while문 무한반복 하다가 언젠가 EOFException이 발생하는 순간 이쪽으로 들어옴
System.out.println("파일을 다 읽었습니다.");
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// catch문은 한 구문이 출력된 후 try ~ catch문을 빠져나오기 때문에 후에는 프로그램 종료가 출력됨
System.out.println("프로그램 종료");
}
사용자가 원하는 파일 이름을 받아서 출력하고 싶다면
// Run클래스의 fileSave(매개변수) 이용
ObjectsDao ods = new ObjectsDao();
ods.fileSave("phones.txt");
// ObjectsDao 클래스에서 매개변수(String fileName) 추가
public void fileSave(String fileName) {
phones.txt는 예시로 넣은 거고, 실제로는 스캐너를 이용하여 사용자의 입력을 받아서 넣어야겠져