자바에서 입출력
I/O란 Input과 Output의 약자로 입력과 출력, 간단히 줄여서 입출력이라고 한다. 입출력은 컴퓨터 내부 또는 외부의 장치와 프로그램 간의 데이터를 주고받는 것을 말한다.
스트림(stream)
자바에서 어느 한쪽에서 다른 쪽으로 데이터를 전달하려면, 두 대상을 연결하고 데이터를 전송할 수 있는 무언가가 필요한데 이것을 스트림(stream)이라고 정의했다.
스트림이란 데이터를 운반하는데 사용되는 연결통로이다.
스트림은 단방향통신만 가능하기 때문에 하나의 스트림으로 입력과 출력을 동시에 처리할 수 없다.
스트림은 먼저 보낸 데이터를 먼저 받게 되어 있으며 중간에 건너뜀 없이 연속적으로 데이터를 주고받는다.
큐(queue)와 같은 FIFO(First In First Out) 구조로 되어 있다고 생각하면 이해하기 쉽다.
바이트 기반 스트림 - InputStream, OutputStream
스트림은 바이트단위로 데이터를 전송하며 입출력 대상에 따라 다음과 같은 입출력 스트림이 있다.
입력스트림 | 출력스트림 | 입출력 대상의 종류 |
FileInputStream | FileOutputStream | 파일 |
ByteArrayInputStream | ByteArrayOutputStream | 메모리(byte배열) |
PipedInputStream | PipedOutputStream | 프로세서(프로세스간의 통신) |
AudioInputStream | AudioOutputStream | 오디오장치 |
자바에서는 java.io패키지를 통해서 많은 종류의 입출력관련 클래스들을 제공하고 있으며, 입출력을 처리할 수 있는 표준화된 방법을 제공함으로써 입출력의 대상이 달라져도 동일한 방법으로 입출력이 가능하기 때문에 프로그래밍을 하기에 편리하다.
InputStream | OutputStream |
abstract int read() | abstract void write(int b) |
int read(byte[ ] b) | void write(byte[ ] b) |
int read (byte[ ] b, int off, int len) | void write(byte[ ] b, int off, int len) |
read()의 반환타입이 byte가 아니라 int인 이유는 read()의 반환 값의 범위가 0~255와 -1이기 때문이다.
InputStream의 read()와 OutputStream의 write(int b)는 입출력의 대상에 따라 읽고 쓰는 방법이 다를 것이기 때문에 상황에 알맞게 구현하라는 의미에서 추상 메서드로 정의되어 있다.
보조 스트림
스트림의 기능을 보완하기 위한 보조스트림이 제공된다. 보조 스트림은 실제 데이터를 주고받는 스트림이 아니기 때문에 데이터를 입출력할 수 있는 기능은 없지만, 스트림의 기능을 향상하거나 새로운 기능을 추가할 수 있다. 그래서 보조 스트림만으로는 입출력을 처리할 수 없고, 스트림을 먼저 생성한 다음에 이를 이용해서 보조 스트림을 생성해야 한다.
// 먼저 기반스트림을 생성한다.
FileInputStream fis = new FileInputStream("test.txt");
// 기반스트림을 이용해서 보조스트림을 생성한다.
BufferedInputStream bis = new BufferedInputStream(fis);
bis.read();
코드 상으로는 보조스트림인 BufferedInputStream이 입력 기능을 수행하는 것처럼 보이지만, 실제 입력 기능은 BufferedInputStream과 연결된 FileInputStream이 수행하고, 보조 스트림인 BufferedInputStream은 버퍼만을 제공한다.
버퍼를 사용한 입출력이 빠르기 때문에 버퍼를 이용한 보조스트림을 사용한다.
문자 기반 | 출력 | 설명 |
FilterInputStream | FilterOutputStream | 필터를 이용한 입출력 처리 |
BufferedInputStream | BufferedOutputStream | 버퍼를 이용한 입출력 성능향상 |
DataInputStream | DataOutputStream | int, flaoat와 같은 기본형 단위(primitive type)로 데이터를 처리하는 기능 |
SequenceInputStream | 없음 | 두 개의 스트림을 하나로 연결 |
LineNumberInputStream | 없음 | 읽어 온 데이터 라인의 번호를 카운트(JDK1.1부터LineNumberReader로 대체) |
ObjectInputStream | ObjectOutputStream | 데이터를 객체단위로 읽고 쓰는데 사용 주로 파일을 이용하며 직렬화와 관련있음 |
없음 | PrintOutputStream | 버퍼를 이용하며, 추가적인 print관련 기능 (print, printf, println메서드) |
pushbackInputStream | 없음 | 버퍼를 이용해서 읽어 온 데이터를 다시 되돌리는 기능(unread, push back to buffer) |
스트림 - Reader, Writer
문자 데이터를 입출력할 때는 바이트 기반 스트림 대신 문자 기반 스트림을 사용하자.
InputStream > Reader
OutputStream > Writer
바이트기반 스트림 | 문자기반 스트림 |
FileInputStream FileOutputStream |
FileReader FileWriter |
ByteArrayInputStream ByteArrayOutputStream |
CharArrayReader CharArrayWriter |
PipedInputStream PipedOutputStream |
PipedReader PipedWriter |
StringBufferInputStream(deprecated) StringBufferOutputStream(deprecated) |
StringReader StringWriter |
StringBufferInputStream, StringBufferOutputStream은 StringReader와 StringWriter로 대체되어 더 이상 사용되지 않는다.
문자기반 스트림의 이름은 바이트 기반 스트림의 이름에서 InputStream은 Reader로 OutputStream은 Writer로만 바꾸면 된다. 단, ByteArrayInputStream에 대응하는 문자 기반 스트림은 char배열을 사용하는 CharArrayReader이다.
Reader와 Writer에서도 역시 추상메서드가 아닌 메서드들은 추상 메서드를 이용해서 작성되었으며, 프로그래밍적인 관점에서 볼 때 read()를 추상 메서드로 하는 것보다 int read(char [] cbuf, int off, int len)를 추상 메서드로 하는 것이 더 바람직하다.
InputStream | Reader |
abstract int read() int read(byte[] b) int read(byte[] b, int off, int len) |
int read() int read(char[] cbuf) abstract int read(char[] chuf, int off, int len) |
OutputStream | Writer |
abstract void write(int b) void write(byte[] b void write(byte[] b, int off, int len) |
void write(int c) void write(char[] cbuf) abstract void write(char[] cbuf, int off, int len) void write(String str) void write(String str, int off, int len) |
바이트기반 보조스트림 | 문자기반 보조스트림 |
BufferedInterStream BufferedOutputStream |
BufferedReader BufferedWriter |
FilterInputStream FilterOutputStream |
FilterReader FilterWriter |
LineNumberInputStream(deprecated) | LineNumberReader |
PrintStream | PrintWriter |
PushbackInputStream | PushbackReader |