입출력 (IO) (23.05.12~13)

·2023년 5월 12일
0

Java

목록 보기
30/35
post-thumbnail

📝 입출력(IO)


💡 입출력(IO)

Input과 Output의 약자
컴퓨터 내부 또는 외부 장치와 프로그램 간의 데이터를 주고 받는 것

  • 장치와 입출력을 위해서는 하드웨어 장치에 직접 접근이 필요한데,
    다양한 매체에 존재하는 데이터들을 사용하기 위해 입출력 데이터를 처리할 공통적인 방법으로 스트림 이용

✏️ 주요 용어

  • Input (입력)
    : 외부 -> 내부로 데이터를 들여오는 것

  • Output (출력)
    : 내부 -> 외부로 데이터를 내보내는 것

  • Stream
    : 입/출력 통로 역할(데이터가 흘러가는 통로)
    -> 기본적으로 Stream은 단방향

Stream 클래스

입출력 장치에서 데이터를 읽고 쓰기 위해서 자바에서 제공하는 클래스

  • 모든 스트림은 단방향이며 각각의 장치마다 연결할 수 있는 스트림 존재
  • 하나의 스트림으로 입출력을 동시에 수행할 수 없으므로 동시에 수행하려면 2개의 스트림 필요
  • Stream 클래스의 분류

✏️ Stream 클래스 주요 기능

  • 변수명.write(char c)
    : 한 문자씩 출력

  • 변수명.write(String s)
    : 한 줄씩 출력

  • 변수명.close()
    : 통로에 남아 있는 내용을 모두 내보내고 통로를 없앰

✏️ 예제 1 - 파일 출력 (내부 == 프로그램, 외부 == 파일)

  • IOService 클래스
package edu.kh.io.model.service;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;

public class IOService {

	// IO
	
	// Input (입력) : 외부 -> 내부로 데이터를 들여오는 것
	// Output (출력) : 내부 -> 외부로 데이터를 내보내는 것
	
	// Stream : 입/출력 통로 역할(데이터가 흘러가는 통로)
	//			기본적으로 Stream은 단방향
	
	
	// 1) 파일 출력 (내부 == 프로그램, 외부 == 파일)
	public void output1() {
		
		FileOutputStream fos = null;
		// FileOutputStream 객체 생성 시
		// FileNotFoundException 예외가 발생할 가능성이 있음 -> 예외 처리 필요
		
		try {
			fos = new FileOutputStream("test1.txt");
			// 현재 프로그램에서
			// test1.txt 파일로 출력하는 통로 객체 생성
			
			// 파일에 "Hello" 내보내기
			String str = "Hello";
			
			for(int i=0; i<str.length(); i++) {
				
				System.out.println( str.charAt(i) );
				
				// "Hello"를 한 문자씩 끊어서 파일로 출력하기
				fos.write( str.charAt(i) );
				
				// write()는 IOException을 발생시킬 가능성이 있다.
			}
			
		} catch(IOException e) {
			System.out.println("예외 발생");
			e.printStackTrace(); // 예외 추적
			
		} finally {
			
			// 예외가 발생하든 말든 무조건 수행
			
			// 사용한 스트림 자원 반환(통로 없앰) --> 필수 작성!
			// 프로그램 메모리 관리 차원에서 항상 다 쓰면 끊어 줌
			// -> 작성 안 하면 문제점으로 꼽을 수 있다.
			
			try {
				fos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
  • test1.txt (목적지 역할)

✏️ 예제 2 - 파일 출력(문자 기반 스트림)

  • IOService 클래스
	public void output2() {
		
		FileWriter fw = null; // 프로그램 -> 파일로 쓰는 문자 기반 스트림
		
		try {
			fw = new FileWriter("test2.txt", true); // 외부 파일과 연결하는 스트림 객체 생성
		// 	fw = new FileWriter("경로", 이어쓰기 옵션);
								// -> byte 기반 스트림도 사용 가능한 옵션
			
			
			String str = "안녕하세요. Hello. 1234 !#!!\n";
			
			// fw.write(int c) : 한 문자씩 출력
			// fw.write(String s) : 한 줄씩 출력
			
			fw.write(str);
			// 실행했는데 출력이 안 된다.. 왜일까?
			// -> 한 줄을 통째로 보내기 위해 "버퍼"를 이용하는데
			// 아직 버퍼에 담겨 있음 -> 이걸 강제로 밀어넣어서 버퍼를 비워야 함
			
			// close() 구문을 수행하면 통로에 남아 있는 내용을 모두 내보내고
			// 통로를 없앤다!
			
		} catch(IOException e) {
			e.printStackTrace(); // 예외 추적
		} finally {
			
			try {
				fw.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
	}
  • test2.txt (목적지 역할)

✏️ 예제 3 - 파일 입력 : 외부(파일) -> 내부(프로그램)으로 읽어 오기

  • IOService 클래스
	public void input1() {
		
		FileInputStream fis = null; // 파일 -> 프로그램으로 읽어 오는 바이트 기반 스트림
		
		try {
			fis = new FileInputStream("test1.txt");
			
			// FileInputStream은 1byte씩만 읽어 올 수 있다.
			while(true) {
				
				int data = fis.read(); // 다음 1byte를 읽어 오는데 정수형임
									   // 다음 내용이 없으면 -1 반환
				
				if(data == -1) { // 다음 내용 없음 -> 종료
					break;
				}
				
				// 반복 종료 안 됐으면 char로 강제 형변환하여 문자로 출력
				System.out.println((char)data);
			}
			
		} catch(IOException e) {
			
		} finally {
			try {
				fis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
  • 출력 화면

✏️ 예제 4 - 파일 입력(문자 기반 스트림)

  • IOService 클래스
	public void input2() {
		
		FileReader fr = null;
		
		try {
			
			fr = new FileReader("test2.txt");
			
			while(true) {
				
				int data = fr.read(); // 다음 한 문자를 읽어 옴. 없으면 -1
				
				if(data == -1) {
					break;
				}
				
				System.out.print((char)data);
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				fr.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
	}
  • 출력 화면

📝 직렬화와 역직렬화

💡 직렬화(Serialization)

  • Serializable 인터페이스를 implements하여 구현
  • 객체 직렬화 시 private 필드를 포함한 모든 필드를 바이트로 변환하지만
    transient 키워드를 사용한 필드는 직렬화에서 제외

💡 역직렬화(Deserialization)

  • 직렬화된 객체를 역직렬화할 때는 직렬화 했을 때와 같은 클래스 사용
  • 단, 클래스 이름이 같더라도 클래스 내용이 변경된 경우 역직렬화 실패
  • readObject(객체)
    : 직렬화된 객체 데이터를 읽어 와서 역직렬화 시켜 정상적인 객체 형태로 반환함

  • writeObject(객체)
    : 출력하려는 객체는 직렬화가 가능해야만 함
    -> Serializable 인터페이스 구현 필수

💡 ObjectXXXStream

객체를 파일 또는 네트워크를 통해 입/출력할 수 있는 스트림

  • ObjectOutputStream
    : 객체를 바이트 기반 스트림으로 출력할 수 있게 하는 스트림
    -> 조건 : 출력하려는 객체에 직렬화 가능 여부를 나타내는 Serializable 인터페이스를 상속받아야 한다.

  • ObjectInputStream
    : 객체를 바이트 기반 스트림으로 입력할 수 있게 하는 스트림

💡 Serializable

직렬화가 될 수 있는 객체가 상속받는 인터페이스

  • ObjectOutputStream이 객체를 내보낼 때 해당 인터페이스를 상속받은 객체인 경우만 직렬화 작업 진행
  • Serializable을 상속했지만 구현할 메소드가 없음 == 마커 인터페이스 (표시 용도의 인터페이스)


✏️ 예제 5 - 객체 출력 보조 스트림

  • Student 클래스
package edu.kh.io.dto;

import java.io.Serializable;

public class Student implements Serializable {

	private String name;
	private int grade;
	private int classRoom;
	private int number;
	private char gender;
	
	public Student() {}

	public Student(String name, int grade, int classRoom, int number, char gender) {
		super();
		this.name = name;
		this.grade = grade;
		this.classRoom = classRoom;
		this.number = number;
		this.gender = gender;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getGrade() {
		return grade;
	}

	public void setGrade(int grade) {
		this.grade = grade;
	}

	public int getClassRoom() {
		return classRoom;
	}

	public void setClassRoom(int classRoom) {
		this.classRoom = classRoom;
	}

	public int getNumber() {
		return number;
	}

	public void setNumber(int number) {
		this.number = number;
	}

	public char getGender() {
		return gender;
	}

	public void setGender(char gender) {
		this.gender = gender;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", grade=" + grade + ", classRoom=" + classRoom + ", number=" + number
				+ ", gender=" + gender + "]";
	}
	
}
  • IOService 클래스
	public void objectOutput() {
		
		// ObjectXXXStream : 객체를 파일 또는 네트워크를 통해
		//					 입/출력할 수 있는 스트림
		
		// ObjectOutputStream
		// -> 객체를 바이트 기반 스트림으로 출력할 수 있게 하는 스트림
		// 	  조건 : 출력하려는 객체에 직렬화 가능 여부를 나타내는
		//		    Serializable 인터페이스를 상속받아야 한다.
		
		ObjectOutputStream oos = null;
		
		try {
			oos = new ObjectOutputStream(new FileOutputStream("object/Student.txt"));
			
			// 내보낼 학생 객체 생성
			Student s = new Student("홍길동", 3, 5, 7, '남');
			
			// 학생 객체를 파일로 출력
			oos.writeObject(s);
			
			System.out.println("학생 출력 완료");
			
		} catch(Exception e) {
			e.printStackTrace();
		} finally {
			
				try {
					if(oos != null) oos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
		
	}
  • Student.txt

객체 내용이 직렬화되어 보여진다.


✏️ 예제 6 - 객체 입력 보조 스트림

	public void objectInput() {
		
		ObjectInputStream ois = null;
		
		try {
			
			ois = new ObjectInputStream(new FileInputStream("object/Student.txt"));
			
			
			Student s = (Student)ois.readObject();
			// readObject() : 직렬화된 객체 데이터를 읽어 와서
			//				  역직렬화 시켜 정상적인 객체 형태로 반환함
			// throws IOException, ClassNotFoundException
			
			System.out.println(s);
			
		} catch(Exception e) {
			e.printStackTrace();
		} finally {
			
				try {
					if(ois != null) ois.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}
  • 출력 화면

직렬화된 객체를 역직렬화 시켜 정상적인 객체로 반환된 모습을 볼 수 있다.


✏️ 예제 7 - List에 Student 객체를 담아서 파일로 출력

	public void listOutput() {
		
		ObjectOutputStream oos = null;
		
		try {
			
			oos = new ObjectOutputStream(new FileOutputStream("object/studentList.ini"));
			
			List<Student> list = new ArrayList<>();
			
			list.add(new Student("A", 1, 1, 1, '여'));
			list.add(new Student("B", 2, 2, 2, '여'));
			list.add(new Student("C", 3, 3, 3, '남'));
			list.add(new Student("D", 1, 2, 3, '남'));
			
			oos.writeObject(list);
			// writeObject(객체) : 출력하려는 객체는 직렬화가 가능해야만 한다.
			//					  Serializable 인터페이스 구현 필수
            
			// 컬렉션은 모두 직렬화가 가능하도록 Serializable 인터페이스 구현이 되어 있는 상태임
			// -> 단, 컬렉션에 저장하는 객체가 직렬화 가능하지 않다면
			//	  출력이 되지 않는다. (NotSerializableException 발생)
			
			System.out.println("학생 출력 완료");
			
		} catch(Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if(oos != null) oos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
  • studentList.ini


✏️ 예제 8 - Output으로 내보낸 객체 Input으로 읽어 오기

	public void listInput() {
		
		ObjectInputStream ois = null;
		
		try {
			
			ois = new ObjectInputStream(new FileInputStream("object/studentList.ini"));
			
			List<Student> list = (List<Student>)ois.readObject();

			for(Student s : list) {
				System.out.println(s);
			}
			
		} catch(Exception e) {
			e.printStackTrace();
		} finally {
			
			try {
				if(ois != null) ois.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
  • 출력 화면

profile
풀스택 개발자 기록집 📁

0개의 댓글