[자바 기초] 입출력 I/O(1) - 기본

입출력이란?

I/O란 컴퓨터 내부 또는 외부의 장치와 프로그램간의 데이터를 주고 받는 것을 말한다. 예를 들면 키보드로부터 데이터를 입력받는다든가 화면에 출력한다던가 하는 것이다.

Stream

자바에서 입출력을 수행하려면, 즉 어느 한쪽에서 다른 쪽으로 데이터를 전달하려면, 두 대상을 연결하고 데이터를 전송할 수 있는 무언가가 필요한데 이를 스트림이라고 정의한다.

데이터를 운반하는데 사용되는 연결통로

스트림은 단방향통신만 가능하기 때문에 하나의 스트림으로 입력과 출력을 동시에 처리할 수 없다. 그래서 입출력을 동시에 수행하려면 입력스트림과 출력스트림, 2개의 스트림이 필요하다.

스트림은 먼저 보낸 데이터를 먼저 받게 되어 있으며 중간에 건너뜀 없이 연속적으로 데이터를 주고 받는다. 큐와 같은 FIFO 구조로 되어있다고 생각하면 된다.

바이트기반 스트림 - InputStream, OutputStream

스트림은 바이트단위로 데이터를 전송하며 입출력 대상에 따라 다음과 같은 입출력스트림이 있다.

FileInputStream, FileOutputStream, 파일

ByteArrayInputStream, ByteArrayOutputStream, 메모리(byte배열)

PipedInputStream, PipedOutputStream, 프로세스(IPC)

AudioInputStream, AudioOutputStream, 오디오장치

자바에서는 java.io패키지를 통해서 많은 종류의 입출력관련 클래스들을 제공하고 있으며, 입출력을 처리할 수 있는 표준화된 방법을 제공함으로써 입출력의 대상이 달라져도 동일한 방법으로 입출력이 가능하기 때문에 프로그래밍을 하기에 편리하다.

Inputstream

abstract int read()

int read(byte[] b)

int read(byte[] b, int off, int len)

OutputStream

abstract void write(int b)

void write(byte[] b)

void write(byte[] b, int off, int len)

InputStream의 read()와 OutputStream의 write(int b)는 입출력의 대상에 따라 읽고 쓰는 방법이 다를 것이기 때문에 각 상황에 알맞게 구현하라는 의미에서 추상메서드로 정의되어 있다.

read()와 write(int b)를 제외한 나머지 메서드들은 추상메서드가 아니니까 굳이 추상메서드인 read()와 write(int b)를 구현하지 않아도 이들을 사용하면 될 것이라고 생각할 수 있지만 사실 추상메서드인 read()와 write(int b)를 이용해서 구현한 것이기 떄문에 read()와 write(int b)를 구현하지 않으면 아무런 의미가 없는 메서드들이 된다.

public abstract class InputStream {
	...
	// 입력스트림으로 부터 1 byte를 읽어서 반환한다. 읽을 수 없으면 -1을 반환한다.
	abstract int read();

	// 입력스트림으로부터 len개의 byte를 읽어서 byte배열 b의 off위치부터 저장한다.
	int read(byte[] b, int off, int len){
		...
		for(int i = off; i < off + len; i++){
			// read()를 호출해서 데이터를 읽어서 배열을 채운다.
			b[i] = (byte)read();
		}
		,,,
	}
	
	// 입력스트림으로부터 byte배열 b의 크기만큼 데이터를 읽어서 배열 b에 저장한다.
	int read(byte[] b) {
		return read(b, 0, b.length);
	}
}

보조 스트림

스트림의 기능을 보완하기 위한 보조스트림이 제공된다. 보조스트림은 실제 데이터를 주고받는 스트림이 아니기 때문에 데이터를 입출력할 수 있는 기능은 없지만, 스트림의 기능을 향상시키거나 새로운 기능을 추가할 수 있다. 그래서 보조스트림만으로는 입출력을 처리할 수 없고, 스트림을 먼저 생성한 다음에 이를 이용해서 보조스트림을 생성해야한다.

FileInputStream fis = new FileInputStream("test.txt");

BufferedInputStream bis = new BufferedInputStream(fis);

bis.read()

실제 입력기능은 BufferedInputSteam과 FileInputStream이 수행하고, 보조스트림인 BufferedInputStream은 버퍼만을 제공한다. 버퍼를 사용한 입출력과 사용하지 않은 입출력간 성능차이는 상당하기 떄문에 대부분의 경우에 버퍼를 이용한 보조스트림을 사용한다.

BufferedInputStream, DataInputStream, DigestInputStream, LineNumberInputStream, PushbackInputStream은 모두 FilterInputStream의 자손들이고, FilterInputStream은 InputStream의 자손이라서 결국 모든 보조스트림 역시 InputStream과 OutputStream의 자손들이므로 입출력방법이 같다.

FilterInputStream, FilterOutputStream, 필터를 이용한 입출력 처리

BufferedInputStream, BufferedOutputStream, 버퍼를 이용한 입출력 성능향상

DataInputStream, DataOutputStream, int, float과 같은 기본형 단위(primitive type)로 데이터를 처리하는 기능

SequenceInputStream, 없음, 두 개의 스트림을 하나로 연결

LineNumberInputStream, 없음, 읽어온 데이터의 라인 번호를 카운트 (JDK 1.1부터 LineNumberReader로 대체)

ObjectInputStream, ObjectOutputStream, 데이터를 객체단위로 읽고 쓰는데 사용. 주로 파일을 이용하며 객체 직렬화와 관련있음.

없음, PrintStream, 버퍼를 이용하며, 추가적인 print관련 기능(print, printf, println메서드)

PushbackIntputStream, 없음, 버퍼를 이용해서 읽어 온 데이터를 다시 되돌리는 기능(unread, push back to buffer)

문자기반 스트림 - Reader, Writer

바이트기반이라 함은 입출력의 단위가 1 byte라는 뜻이다. Java에서는 한 문자를 의미하는 char형이 1 byte가 아니라 2 byte이기 때문에 바이트기반의 스트림으로 2 byte인 문자를 처리하는 데는 어려움이 있다. 이 점을 보완하기 위해서 문자기반의 스트림이 제공된다. 문자데이터를 입출력할 때는 바이트기반 스트림 대신 문자기반 스트림을 사용한다,.

FileReader

FileWriter

CharArrayReader

CharArrayWriter

PipedReader

PipedWriter

StringReader

StringWriter

Reader

int read()

int read(char[] cbuf)

abstract int read(char[] cbuf, int off, int len)

Writer

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)

보조스트림

BufferedReader

BufferedWriter

FilterReader

FilterWriter

LineNumberReader

PrintWriter

PushbackReader