본문 바로가기
개발입문/JAVA 세미프로젝트

[JAVA] Stream을 이용하여 파일 저장이 가능한 전화번호부 만들기

by 양히◡̈ 2022. 10. 12.

이번에는 입력한 데이터 값이 프로그램을 종료하면 파일에 저장이 가능하도록 만들었다.

외부데이터에 저장하고 저장된 데이터를 다시 가져오는 작업이 필요하므로 직렬화와 역직렬화의 과정이 포함된다.

 

import java.io.Serializable;

public class PhoneInfo implements Serializable { 
	
	private String name; 
	private String phoneNum;
	
	PhoneInfo (String name, String phoneNum) {
		this.name = name;
		this.phoneNum = phoneNum;
	}

	public void showPhoneInfo() {
		System.out.println("이름 : " + name);
		System.out.println("전화번호 : " + phoneNum);
	}
	
	public String getName() {
		return name;
	}
	
	public String getPhoneNum() {
		return phoneNum;
	}
	
	
	
	@Override 
	public int hashCode() {
		return name.hashCode();
	}
	
	
	@Override
	public boolean equals(Object obj) {
		if ( obj instanceof PhoneInfo ) {
			PhoneInfo pi = (PhoneInfo) obj;
			if ( pi.getName().equals(name) ) {
				return true;
			}
		}
		return false;
	}

	public boolean contains(String name) {
		
		return false;
	}
}

 

PhoneInfo 클래스 내부에 수정된 건 없지만, 이번에는 외부 데이터를 저장하고 입력받기 위해 직렬화와 역직렬화의 과정을 거쳐야 하므로 Sreializable 인터페이스를 상속받기 위해 impliments 했다.

 

 

public class PhoneUnivInfo extends PhoneInfo {
	
	private String major;
	private int grade;
	
	PhoneUnivInfo (String name, String phoneNum, String major, int grade) {
	super(name, phoneNum);
	this.major = major;
	this.grade = grade;
}
	@Override
	public void showPhoneInfo() {
		super.showPhoneInfo();
		System.out.println("전공 : "+major);
		System.out.println("학년 : "+grade);
	}
}
public class PhoneCompanyInfo extends PhoneInfo {
		
	private String company;	
	
		PhoneCompanyInfo(String name, String phoneNum, String company) {
		super(name, phoneNum);
		this.company = company;
	}

		@Override
		public void showPhoneInfo() { 
			super.showPhoneInfo();
			System.out.println("회사 : "+company);
		}
}

 

PhoneInfo 의 자손 클래스이므로 따로 수정해야할 부분은 없었다.

 

 

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashSet;
import java.util.Iterator;

public class PhoneBookManager {

	static HashSet<PhoneInfo> list = new HashSet<PhoneInfo>();

	// 싱글톤 : 하나의 인스턴스만 만들 수 있도록하여 공유함
	private PhoneBookManager() {
	}

	private static PhoneBookManager instance;

	public static PhoneBookManager getInstance() {
		if (instance == null)
			instance = new PhoneBookManager();
		return instance;
	}

	public void readData() {

		System.out.println("데이터 입력을 시작합니다.");
		System.out.println("1.일반, 2.대학, 3.회사");

		int kinds = PhoneBook.sc.nextInt();
		PhoneBook.sc.nextLine();

		System.out.print("이름 : ");
		String name = PhoneBook.sc.nextLine();
		System.out.print("전화번호 : ");
		String phoneNum = PhoneBook.sc.nextLine();

		PhoneInfo pi = null;

		switch (kinds) {

		case 1:
			pi = new PhoneInfo(name, phoneNum);
			break;
		case 2:
			System.out.print("전공 : ");
			String major = PhoneBook.sc.nextLine();
			System.out.print("학년 : ");
			int grade = PhoneBook.sc.nextInt();
			pi = new PhoneUnivInfo(name, phoneNum, major, grade);
			break;
		case 3:
			System.out.print("회사 : ");
			String company = PhoneBook.sc.nextLine();
			pi = new PhoneCompanyInfo(name, phoneNum, company);
			break;
		}
		list.add(pi);

		System.out.println("데이터 입력이 완료되었습니다..");
	}

	public void searchData() {
		System.out.print("데이터 검색을 시작합니다.\n검색할 이름 : ");
		String name = PhoneBook.sc.nextLine();

		Iterator<PhoneInfo> itr = list.iterator();

		while (itr.hasNext()) {
			PhoneInfo pi = itr.next();
			if (name.equals(pi.getName())) {
				pi.showPhoneInfo();
				System.out.println("데이터 검색이 완료되었습니다.");
			}
		}
	}

	public void deleteData() {
		System.out.println("데이터 삭제를 시작합니다.\n삭제할 이름 : ");
		String name = PhoneBook.sc.nextLine();

		Iterator<PhoneInfo> itr = list.iterator();

		while (itr.hasNext()) {
			PhoneInfo pi = itr.next();
			if (name.equals(pi.getName())) {
				list.remove(pi);
				System.out.println("데이터가 삭제되었습니다.");
			}
		}
	}

	public void printAllData() {
		System.out.println("데이터를 전체 출력합니다.");
		System.out.println("현재 저장된 데이터의 개수는 " + list.size() + "개 입니다.");

		Iterator<PhoneInfo> itr = list.iterator();

		while (itr.hasNext()) {
			PhoneInfo pi = itr.next();
			pi.showPhoneInfo();
		}
		System.out.println("--------------------------");
	}

	public void readFromFile() {
		// 전제, 파일이 있는지 없는지 확인.
		// 있다면, 직렬화로 저장된 객체를 읽어서 해쉬셋 요소로 등록하기.
		ObjectInputStream objInput = null;
		FileInputStream fileInput = null;

		String path = System.getProperty("user.dir");
		// user.dir : 현재 프로젝트 파일의 경로 찾기
		System.out.println("path : " + path);
		File file = new File(path + "\\PhoneBook.txt");

		if (!file.exists()) { // 파일이 없다면 만들어라
			try {
				file.createNewFile();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		// 폴더와 파일이 생성됐다면, 해당 파일을 스트림으로 변환하여 데이터의 저장 처리할 예정.
		try {
			fileInput = new FileInputStream(file);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		// 파일에 데이터가 있는지 확인
		try {
			if (fileInput.available() > 0) { // 읽을 데이터가 있다면
				// available은 데이터의 남은 바이트를 int로 반환하고
				// 스트림의 끝(eof)에 도달하면 0을 반환함
				objInput = new ObjectInputStream(fileInput);

				while (fileInput.available() > 0) {
					// 더 이상의 데이터가 없을 때까지 반복하며 리스트에 추가해라
					list.add((PhoneInfo) objInput.readObject());
				}
				objInput.close();
			} else {
				System.out.println("저장된 데이터가 없습니다.");
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			fileInput.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void storeToFile() {
		String path = System.getProperty("user.dir");
		File file = new File(path + "\\PhoneBook.txt");

		if (list != null) { // 리스트에 내용이 있다면
			Iterator<PhoneInfo> itr = list.iterator();

			ObjectOutputStream objOut = null;
			FileOutputStream fileOut = null;

			try {
				fileOut = new FileOutputStream(file);
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
			try {
				objOut = new ObjectOutputStream(fileOut);
			} catch (IOException e) {
				e.printStackTrace();
			}

			while (itr.hasNext()) {
				PhoneInfo p = itr.next();
				try {
					objOut.writeObject(p);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			try {
				objOut.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				fileOut.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

 

기존과 내용은 동일하고, 외부에 저장된 데이터를 불러오는 readFromFile 메소드와 입력된 데이터를 외부 데이터에 저장하는 StroreToFile 메소드를 추가했다.

 

throws 를 사용해 예외처리를 할 수도 있지만, 일단은 예외처리를 어떤 부분에서 했는지를 각각 볼 수 있도록 복잡하더라도 일일이 try/catch를 사용했다.

 

 

 

import java.util.Scanner;

public class PhoneBook {

	static Scanner sc = new Scanner(System.in);

	public static void main(String[] args) {

		PhoneBookManager manager = PhoneBookManager.getInstance(); // 싱글톤
		manager.readFromFile();
		int ch;
		boolean flag = true;

		while (flag) {

			showMenu();

			ch = sc.nextInt();
			sc.nextLine(); // 엔터소거용

			switch (ch) {
			case 1:
				manager.readData();
				break;
			case 2:
				manager.searchData();
				break;
			case 3:
				manager.deleteData();
				break;
			case 4:
				manager.printAllData();
				break;
			case 5:
				System.out.println("프로그램을 종료합니다.");
				manager.storeToFile();
				flag = false;
				sc.close();
				break;
			}// end_switch

		} // end_while

	}// end_main

	static void showMenu() {
		System.out.println("메뉴를 선택하세요.");
		System.out.println("----------------");
		System.out.println("1.데이터 입력");
		System.out.println("2.데이터 검색");
		System.out.println("3.데이터 삭제");
		System.out.println("4.데이터 전체출력");
		System.out.println("5.프로그램 종료");
		System.out.println("----------------");
	}
}

 

프로그램이 실행되면 바로 저장된 데이터를 불러오도록 readFromFile 메소드를 위에 배치하였고,

반대로 프로그램이 끝나기 직전에 데이터를 저장하도록 storeToFile 메소드를 case 5 (프로그램 종료) 에 배치하였다.

데이터를 저장하고 나서 while문을 빠져나가도록 boolean flag를 false로 만들었다.

 

댓글