IO(Input & Output: 입출력)
항상 기준은 프로그램임!
외부 매체는 파일, 모니터, 스피커 등이 있으나 일단 제일 간단하게 실습은 "파일"로 진행
(기준)
프로그램 ===> 파일
프로그램 <=== 파일
데이터가 움직이기 위해서는 통로가 필요한데 이 통로를 "스트림"이라고 함
<스트림의 특징>
- 단방향(입력, 출력 스트림이 따로 존재해야 함)
- 선입선출(먼저 전달한 값이 먼저 나오는 Queue 구조를 가지고 있음)
-이러한 특징 때문에 시간 지연(delay)가 발생할 수 있음
<스트림의 구분>
- 통로의 사이즈
바이트스트림: 한 번에 1byte짜리만 이동할 수 있는 좁은 통로 => 입력(XXXInputStream) / 출력(XXXOutputStream)
문자스트림: 한 번에 2byte짜리가 이동할 수 있는 넓은 통로 => 입력(XXXReader) / 출력(XXXWriter)
- 외부 매체와의 직접적인 연결 여부
기반스트림: 외부 매체와 직접적으로 연결되는 통로
보조스트림: 기반스트림만으로 부족한 성능을 향상시켜 주는 용도의 스트림(보조스트림은 기반스트림 없이는 쓸 수 없음)
<기반 스트림을 활용하기 위한 순서 (파일 출력편)>
- 하기 예시는 바이트스트림이며, 문자 스트림 사용 시 FileOutputStream메소드 대신 FileWriter 메소드를 사용하면 됨
0. 변수 선언 및 null값 초기화(지역 변수 이슈 때문에 연결 끊을 때 변수명 가져다 쓰려면 선언 위치를 try 밖으로 빼야 함)
FileOutputStream fout = null;
try {
1. FileOutputStream 객체 생성 ( == 연결 통로 만들기)
fout = new FileOutputStream("a_byte.txt");
- 존재하지 않는 파일명을 적을 시 파일 생성 후 통로 만들어짐
- 존재하는 파일명을 적을 시 바로 통로 만들어짐
2. 연결 통로로 데이터를 출력: write() 메소드 사용
fout.write(97);
fout.write('b');
} catch (FileNotFoundException e) {
} catch (IOException e) {
e.printStackTrace();
3. 스트림을 다 이용했으면 자원 반납 (== 연결 끊기, finally 구문 이용)
} finally {
try {
fout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
<기반 스트림을 활용하기 위한 순서 (파일 입력편)>
- 하기 예시는 바이트스트림이며, 문자 스트림 사용 시 FileInputStream메소드 대신 FileReader 메소드를 사용하면 됨
0. 변수 선언 및 null값 초기화(지역 변수 이슈 때문에 연결 끊을 때 변수명 가져다 쓰려면 선언 위치를 try 밖으로 빼야 함)
FileInputStream fin = null;
try {
1. FileInputStream 객체 생성 ( == 연결 통로 만들기)
fin = new FileInputStream("a_byte.txt");
- 존재하지 않는 파일명을 적을 시 오류 남!!
- 반드시 존재하는 파일명을 적어야 함
2. 읽어들이기 (== 입력받기: read() 메소드)
System.out.println(fin.read());
- 를 사용해도 무방하긴 하나 반복문 활용하는 게 더 좋음
반복문 활용법 (1) 무한반복
while (true) {
int value = fin.read();
if(value == -1) {
break;
}
System.out.println(value);
}
반복문 활용법 (2) 조건식 내부에 변수 대입 구문 활용 -- 권장되는 방법
int value = 0;
while((value = fin.read()) != -1) {
System.out.println(value);
}
} catch (FileNotFoundException e) {
} catch (IOException e) {
e.printStackTrace();
3. 스트림을 다 이용했으면 자원 반납 (== 연결 끊기, finally 구문 이용)
} finally {
try {
fout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
=> 단! 기반 스트림 사용 시 단어 하나하나를 통로를 통해 옮기기 때문에 데이터가 많아질 경우 시간 지연이 더 커짐
=> 이럴 때 통로 중간에 버퍼를 만들어서 하나하나 옮겨진 단어들을 모두 모아놨다가 한 번에 출력하는 게 더 이득!
=> 기반 스트림의 출력 속도 향상됨!
==> 이렇게 기반 스트림의 성능 향상을 해 주는 게 보조 스트림이며 위 설명은 그중 하나인 버퍼드!
보조스트림
: 기반 스트림의 부족한 기능들을 확장시킬 수 있는 스트림
=> 입력용 기반 스트림에는 입력용 보조 스트림을 써야 하고
출력용 기반 스트림에는 출력용 보조 스트림을 써야 함
=> 속도 성능 향상 목적의 보조 스트림: BufferedXXX
1. 기반 스트림 생성
기반스트림클래스명 기반스트림객체명 = new 기반스트림클래스명(파일명);
2. 기반스트림객체를 매개변수 삼아서 보조스트림객체를 생성
보조스트림클래스명 보조스트림객체명 = new 보조스트림클래스명(기반스트림객체);
<보조 스트림을 활용하기 위한 순서 (파일 출력편)>
0. 스트림 변수 선언 및 초기화를 코드 한줄로 줄여서 표현
BufferedWriter bw = null;
try {
1. 기반스트림 객체 생성(메인 연결 통로 만들기)
2. 보조스트림 객체 생성(매개변수로 기반스트림객체를 제시)
=> 1, 2단계 한 번에 표현 가능
bw = new BufferedWriter(new FileWriter((c_buffer.txt"));
3. 출력
bw.write("안녕하세요.");
} catch (IOExeption e) {
e.printStackTrace();
} finally {
4. 자원 반납(반드시) => finally 블록 안에 작성
try {
bw.close();
} catch (IOException e) {
e.printStackTracer();
}
}
<보조 스트림을 활용하기 위한 순서 (파일 입력편)>
0. 스트림 변수 선언 및 null 값으로 초기화
BufferedReader br = null;
1. 객체 생성 == 통로를 만들겠음
br = new BufferedReader(new FileReader("c_buffer.txt"));
2. 입력: BufferedReader 클래스에서 제공하는 메소드로 읽어들이겠다
(System.out.println(br.readLine()); // 줄 단위로 출력해도 무방하나 편의를 위해 반복문 활용)
String value = null; // ""로 초기화해도 무방함
while ((value = br.readLine()) != null) {
System.out.println(value);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch(IOException e){
e.printStackTrace();
} finally {
3. 연결 통로를 끊겠다 == 자원 반납(반드시) => finally 블럭에 작성
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
==> 코드 줄이는 법
<try ~ with ~ resource 구문>
=> try(여기) 스트림 객체 생성 구문을 작성하면 생성 후 try 블럭 내용이 실행된 후 알아서 자원 반납됨
try() {
예외가발생할법한구문;
} catch(예외클래스명 e) {
해당예외발생시실행할구문;
}
try(BufferedReader br = new BufferedReader(new FileReader("c_buffer.txt"))){
// 반복문 활용
String value = null;
while((value = br.readLine()) != null) {
System.out.println(value);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
<보조 스트림을 활용하기 위한 순서 (객체 출력편)>
객체 생성
Phone ph = new Phone("아이폰", 1300000);
0. 스트림 변수 선언 및 null로 초기화
ObjectOutputStream oos = null;
1. 스트림 객체 생성(== 연결 통로를 만들겠다)
try {
oos = new ObjectOutputStream(new FileOutputStream("Phone.txt"));
2. 출력: ObjectOutputStream 객체에서 사용하는 writeObject() 메소드 사용
oos.writeObject(ph);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
} finally {
3. 자원 반납
try {
oos.close();
} catch (IOException e) {
e.printStackTracer();
}
// 직렬화 선언을 하지 않으면 객체를 파일로 내보낼 때 NotSerializableException이 발생
// => 1byte짜리 좁은 통로로 큰 객체가 지나갈 수 없기 때문에 가로로 배열해 주는 과정: 직렬화
public class Phone implements Serializable { // 직렬화 선언
<보조 스트림을 활용하기 위한 순서 (객체 입력편)>
try ~ with ~ recource 구문 버전
try ( ObjectInputStream ois = new ObjectInputStream(new FileInputStream("phone.txt"))) {
Phone ph = (Phone)osi.readObject();
System.out.println(ph);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
<보조 스트림을 활용하기 위한 순서 (객체 배열 출력편)>
try ~ with ~ recource 구문 버전
테스트용 객체 배열
Phone[] arr = new Phone[3]; // [0] [1] [2]
테스트용 데이터 담기
arr[0] = new Phone("아이폰", 1300000);
arr[1] = new Phone("갤럭시", 1500000);
arr[2] = new Phone("플립폰", 2000000);
try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("phones.txt"))){
반복문 이용해서 내보내기
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();
}
}
<보조 스트림을 활용하기 위한 순서 (객체 배열 입력편)>
try ~ with ~ recource 구문 버전
EOFExeption(End of file): 파일 내용이 끝났음에도 자꾸 출력하고자 할 때 발생하는 오류
=> IOException의 자식임
Phone[] ph = new Phone[3];
try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream("phones.txt"))) {
// 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("프로그램 종료");