1. IO와 스트림(Stream)
1)IO란?
IO란 Input과 Output을 뜻합니다. 프로그램으로 들어오는 모든 값을 Input값이라고 하고 밖으로 나가는 값을 Output값이라고 합니다. 이를 입출력이라고부르고 자바에서는 입출력을 처리하기 위해 IO패키지를 제공합니다.
2) 스트림(Stream)이란?
IO패키지를 다루다 보면 스트림 이라는 용어를 자주 볼 수 있습니다. 스트림은 사전적인 의미로는 개울, 시내를 뜻하지만 자바에서는 (2진수)데이터의 흐름을 의미합니다.
스트림, 즉 데이터의 통신은 한 쪽 방향으로만 가능하다는 특징이 있습니다. 그렇기 때문에 입력 스트림과 출력 스트림을 각각 따로 사용해야 하며 먼저 들어온 데이터가 먼저 나가는 FIFO구조를 이루고 있습니다.
스트림은 다앙한 종류가 있는데 크게 분류하면 출력단위가 1byte인 바이트기반 스트림과 출력단위가 문자단위인 문자기반 스트림으로 나누어집니다.
2. 바이트기반 스트림
바이트기반 스트림은 바이트단위(8bit)로 데이터를 입출력하는 스트림입니다.
1) 바이트 기반 스트림의 활용
1byte씩 읽어서 1byte씩 출력합니다. InputStream과 OutputStream이 모든 바이트기반 스트림들의 조상이 되며 InputStream을 상속받는 스트림은 read() 추상 메서드를 구현하고 OutputStream을 상속받는 스트림은 write() 추상메서드를 구현합니다.
바이트 기반 스트림은 클래스의 이름이 InputStream, OutputStream으로 끝나며 데이터를 읽거나 출력할 대상에 따라서 적절한 스트림을 사용합니다.
스트림을 사용하기 위해서는 인스턴스를 생성할 때 생성자의 인자로 데이터를 입력받거나 출력 받을 곳을 넘겨준 다음 적절한 메서드를 사용합니다.
3. 바이트기반 보조 스트림
바이트 기반 보조 스트림은 실제 바이트 기반 스팀의 성능을 향상시키는 역할을 합니다.
1) 바이트기반 보조 스트림의 활용
보조 스트림은 실제 입력 스트림을 도와서 성능을 향상시키는 역할을 합니다. 호스에 물을 부드럽게 하는 연수기를 다는 것처럼 직접 데이터를 이동시키는 것은 아니지만 필요한 성능을 가지고 있습니다.
보조 스트림을 사용하는 방법은 데이터를 전송할 스트림을 선언하고 그 스트림을 인자로 보조 스트림의 생성자에 넣어주면 됩니다.
또한 모든 보조 스트림들은 다른 스트림들과 마찬가지로 InputStream과 OutputStream의 자손으로써 입출력방법이 같으므로 보조 스트림의 참조변수로 입출력합니다.
바이트 기반 스트림과 보조 스트림을 이용해서 파일을 복사하는 프로그램을 작성해보겠습니다. 프로그램 코드를 작성하기 전에 읽어 올 txt 파일을 생성합니다.
package chapter13;
import java.io.*;
public class exam100 {
public static void main(String[] args) throws IOException {
// 데이터 수신
FileInputStream fis = null;
fis = new FileInputStream ("prac.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
// 데이터 출력
FileOutputStream fos = null;
fos = new FileOutputStream("result.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
int data;
while ((data = bis.read())!= -1) {
System.out.println(data);
bos.write(data);
}
bos.close(); // 사용한 스트림은 닫아주기 // 안닫으면 에러남!
bis.close(); // 사용한 스트림은 닫아주기 // 안닫으면 에러남!
}
}
Line 7~10까지 파일 입출력에 관계된 입출력 스트림을 각각 생성합니다. Line 9에서는 읽어올 파일의 경로를 작성해주고 Line10에서는 저장할 파일명을 작성합니다. 미리 생성 되어있으면 덮어쓰고 아직 생성되어있지 않다면 자동으로 생성합니다.
Line 11과 12에서는 각각 버퍼를 사용할 수 있도록 현재 입출력 스트림의 인자를 받아서 보조스트림의 인스턴스를 생성합니다. 입력 스트리에서 읽어온 값이 -1이라는 것은 더이상 읽어올 값이 없다는 것이기 때문에 Line14의 while문에서 입력 값이 -1이 아니면 계속해서 출력할 수 있도록 코드를 작성합니다.
중요한 것은 스트림도 외부와의 연결이기 때문에 반드시 close()를 해야 한다는 것입니다.
프로그램이 종료되면서 자동으로 종료되긴 하지만 24시간 돌리는 프로그램일 경우에는 심각한 낭비를 초래할 수 있습니다. Line17과 18에서 연결한 순서와 반대로 close() 합니다.
4. 문자기반 스트림
문자 기반 스트림은 16비트의 문자나 문자열을 읽고 쓰는 스트림입니다.
1) 문자 기반 스트림의 활용
자바에서 문자를 담는 char형 변수는 2byte의 크기가 있습니다. 그렇게 때문에 바이트기반 스트림으로는 문자의 입출력을 처리하는 데 불편함이 있습니다. 그래서 자바에서는 따로 문자 데이터의 입출력을 다루는 스트림을 제공합니다. Reader와 Writer스트림이 모든문자기반 스트림의 조상이 되며 마찬가지로 이를 상속받는 스트림들은 필요한 추상 메서드를 구현합니다.
문자기반 스트림은 이름 InputStream에서 Reader로, OutputStream에서 Writer로 바뀐 것만 빼면 바이트기반 스트림과 사용방법이 비슷합니다. 적절한 필요에 따라서 스트림을 선택하고 사용합니다.
예를 들어 텍스트 파일을 읽어오고 싶을 때는 FileReader 스트림의 객체를 생성하며 읽어올 파일의 위치를 생성자의 인자로 넘기면 됩니다.
5. 문자기반 보조 스트림
문자 기반 보조 스트림은 문자기반 스트림의 성능을 향상시키는 역할을 합니다.
1) 문자 기반 보조 스트림의 활용
문자 기반 스트림에도 성능을 향상시키기 위한 보조 스트림이 있습니다.
보조 스트림을 사용하려면 문자기반 스트림을 생성한 후 그 인자를 보조 스트림으로 넘겨 주면 됩니다.
6. 스트림의 예외처리
스트림을 사용할 때는 예외 처리를 하는 것이 좋습니다.
입출력의 모든 메서드는 IOException이 발생할 가능성이 있습니다. 그렇게 때문에 반드시 예외처리해야 합니다. 지금까지의 코드에서는 예외를 던져서 처리했지만 실제로는 try-catch문으로 처리하는것이 좋습니다. 덧붙여서 사용한 스트림을 오류 여부와 관계없이 반드시 닫아주기 위해 finally에서 close() 메서드를 사용하는 것이 좋습니다.
package chapter13;
import java.io.*;
public class exam101 {
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
fis = new FileInputStream ("prac.txt");
fos = new FileOutputStream ("result.txt");
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream (fos);
int data;
while ((data = fis.read())!= -1) {
bos.write(data);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
bos.close();
bis.close();
} catch(IOException e ) {
e.printStackTrace();
}
}
}
}
Line12의 try문 안에 데이터를 읽어오는것과 데이터를 전송하는 코드를 넣습니다.
Line22에서 예외가 발생할 경우 예외를 처리합니다.
Line24에서 에외발생여부에 관게없이 사용한 스트림을 닫아주기 위해 finally 구문 안에 close() 메서드를 사용합니다. 마찬가지로 close() 메서드도 예외가 발생할 수 있으므로 try-catch문으로 감싸주었습니다.