1) InputStream과 OutputStream
Stream이란?
컴퓨터 상의 모든 프로그램, 입출력 기기(마우스, 키보드, 모니터, 프린터 등)는
서로 다른 플랫폼 위에 만들어져 있고, 다른 언어를 사용하고 있기도 합니다.
그렇다면, 이 프로그램들과 기계들 사이에서는 어떻게 서로 통신을 하게 될까요?
프로그램의 함수, 구성, 언어와 상관없이 모든 프로그램은 스트림(Stream)이라는
것을 통해서 서로 정보를 주고 받게 됩니다.
스트림은 자바에서만 지원하는 기능이 이니라. OS차원에서 지원하는 기능입니다.
예를 들어서 System.out.println(“아하하하…”); 라는 명령으로 우리는 화면에
글씨를 띄울 수 있습니다. 여기에서 println이라는 메소드가 어떤 특정한 곳으로
글자를 스트림으로 쭉 뿌려주게 되죠. 그러면서 글씨가 화면에 표시되게 되는 겁니다.
여기에서 전달되는 데이터는 가장 하위 레벨에서
‘01101011 01001010 10101010 11101101 …’
이런 식으로 바이트(바이너리)로 변환되어 입출력이 됩니다.
키보드 ----à 운영체제 ----à 프로그램
위의 그림에서 ----à 이 부분이 바로 스트림이죠!
키보드에서 버튼을 누르면 운영체제가 이를 캐치하고 스트림을
워드 프로그램으로 보내주어 제가 이 글을 쓸 수가 있는 것입니다.
스트림에는 크게 바이트 스트림, 문자 스트림, 데이터그램 스트림 이 있지만,
여기에선 우선 바이트와 문자 스트림을 다뤄보기로 하겠습니다.
사실, 문자와 바이트 스트림의 차이는 12라는 정수가 12 그대로 받을 것인지(문자 스트림)
아니면, 0C(12)인 숫자로 받아들여질 것인지(바이트 스트림)를 제외하고는 거의 비슷합니다.
2) Reader(InputStreamReader)와 Writer(OutputStreamWriter)
소켓에서 문자열을 읽어 들일 때 이런 식으로 입력을 받습니다.
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
근데, InputStream 객체 하나로만 입출력 하지 왜 이렇게 많은 객체를 엮어서 입출력을
하는 것일까요? 알아봅시다. InputStream은 바이트(바이너리)의 흐름을 받아들이거나
내보내는 객체입니다. 분명 문자열과 Byte[]는 다른 형식의 데이터입니다.
‘01011010 10100111 …’ 이렇게 생긴 바이트 코드를 가지고 바로 문자로 변환시키려면
인코딩도 생각해야 되겠고, 이걸 일일이 끊어다가 문자로 바꿔서 저장하는 것도 구현을
해야 하겠고… 구현 코드가 상당히 복잡해지겠죠? 그래서 우리가 사용하는 Reader라는
객체가 있습니다. 이 객체는 이러한 바이너리 스트림을 문자로 바꿔서 읽어주는 역할을
하게 되는 거죠. 근데, 이 Reader와 Writer 객체는 한 문자 단위로 입출력을 하게 됩니다.
그러므로 10개의 문자로 이루어진 문자열을 입출력 하려면 write나 read를 10번 호출해야
하겠죠? 이를 위해 다음에 설명드릴 Buffer를 사용하게 됩니다.
제가 이 부분의 제목을 Reader(InputStreamReader)… 라고 했는데 왜 이렇게 했나면,
우리가 흔히 쓰는 InputStreamReader가 Reader를 상속한 하위객체이기 때문입니다.
하위 객체들은 아래와 같이 많습니다.
Reader 클래스의 하위 클래스
InputStreamReader – 문자 스트림을 읽기 위한 클래스
BufferedReader – 다른 Reader들을 버퍼링 하기 위한 클래스
FilterReader – 필터링된 스트림으로부터 읽기 위한 클래스
PipedReader – 파이프Writer로부터 읽기 위한 클래스
ChatArrayReader – 문자 배열로부터 읽기 위한 클래스
StringReader – 문자열로부터 읽기 위한 클래스
3) BufferedReader와 BufferedWriter
위에서 언급한 바와 같이 BufferedReader도 또한 Reader객체의 하위 클래스입니다.
하지만, 단독으로 바로 InputStream을 받아서 쓰지는 않습니다. 다른 하위 객체들
(예를 들어 InputStreamReader)을 인수로 받아들여 그 Reader를 Buffering할 수 있게
도와주는 역할을 하는 것이지요.
BufferedReader와 BufferedWriter는 버퍼에 있는 입출력(IO) 클래스입니다.
그렇다면, 여기서 말하는 버퍼(Buffer)란 무언일까요? 버퍼는 바로 메모리를 뜻합니다.
메모리가 있기 때문에 읽어 들여야 할 대상과 써야 할 대상의 속도 차이 때문에 일어나는
병목현상을 줄일 수 있게 됩니다. BuffererReader의 경우에는 한 줄씩 읽어 들이는
readLine이라는 메소드가 있어서 한 줄을 통째로 읽어 String으로 저장을 할 수 있으므로,
구현이 한 층 더 편해집니다. Buffered를 사용하지 않았다면, 한 문자씩 읽어 들여서
조합을 해야 했겠지요. 그런데 주의할 점이 한가지 있습니다. BufferedWriter의 경우,
프로그램을 끄기 전에 반드시 버퍼를 종료시켜야 합니다. flush()나 close()를 사용하면
버퍼를 닫을 수 있는데, 만약에 프로그램을 그냥 꺼버리게 되면, 이 버퍼는 길을 읽고
다시는 사용할 수 없는 자원이 되겠지요. 특히 네트워크 프로그래밍의 경우에는 반드시
flush 메소드를 호출해주어야 합니다.
BufferedReader의 생성자
BufferedReader(Reader in) : Reader in을 인자로 전달받아 BufferedReader 객체를 생성합니다.
BufferedReader(Reader in, int sz) : Reader in을 인자로 전달받아 BufferedReader 객체를
생성합니다. 여기서 BufferedReader가 내부적으로 사용하는 버퍼의 크기를 sz로 설정합니다.
출처 - 안드로이드사이드
'Programming > Java' 카테고리의 다른 글
자바 제너릭(Generic) (1) | 2012.02.01 |
---|---|
Java Native Interface(JNI) (0) | 2010.08.13 |