Input과 Output의 약자
컴퓨터 내부 또는 외부 장치와 프로그램 간의 데이터를 주고 받는 것
- 장치와 입출력을 위해서는 하드웨어 장치에 직접 접근이 필요한데,
다양한 매체에 존재하는 데이터들을 사용하기 위해 입출력 데이터를 처리할 공통적인 방법으로 스트림 이용
Input (입력)
: 외부 -> 내부로 데이터를 들여오는 것
Output (출력)
: 내부 -> 외부로 데이터를 내보내는 것
Stream
: 입/출력 통로 역할(데이터가 흘러가는 통로)
-> 기본적으로 Stream은 단방향
입출력 장치에서 데이터를 읽고 쓰기 위해서 자바에서 제공하는 클래스
- 모든 스트림은 단방향이며 각각의 장치마다 연결할 수 있는 스트림 존재
- 하나의 스트림으로 입출력을 동시에 수행할 수 없으므로 동시에 수행하려면 2개의 스트림 필요
- Stream 클래스의 분류
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();
}
}
}
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();
}
}
}
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();
}
}
}
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();
}
}
}
- Serializable 인터페이스를 implements하여 구현
- 객체 직렬화 시 private 필드를 포함한 모든 필드를 바이트로 변환하지만
transient 키워드를 사용한 필드는 직렬화에서 제외
- 직렬화된 객체를 역직렬화할 때는 직렬화 했을 때와 같은 클래스 사용
- 단, 클래스 이름이 같더라도 클래스 내용이 변경된 경우 역직렬화 실패
readObject(객체)
: 직렬화된 객체 데이터를 읽어 와서 역직렬화 시켜 정상적인 객체 형태로 반환함
writeObject(객체)
: 출력하려는 객체는 직렬화가 가능해야만 함
-> Serializable 인터페이스 구현 필수
객체를 파일 또는 네트워크를 통해 입/출력할 수 있는 스트림
직렬화가 될 수 있는 객체가 상속받는 인터페이스
- ObjectOutputStream이 객체를 내보낼 때 해당 인터페이스를 상속받은 객체인 경우만 직렬화 작업 진행
- Serializable을 상속했지만 구현할 메소드가 없음 == 마커 인터페이스 (표시 용도의 인터페이스)
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 + "]";
}
}
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();
}
}
}
객체 내용이 직렬화되어 보여진다.
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();
}
}
}
직렬화된 객체를 역직렬화 시켜 정상적인 객체로 반환된 모습을 볼 수 있다.
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();
}
}
}
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();
}
}
}