Intro. 스트림으로 입출력하는 자바
자바 프로그램은 데이터를 읽고 출력하는 입출력(I/O)을 빈번히 수행한다.
Ex. 키보드로 입력, 모니터로 출력 / 파일 입출력 / 네트워크로부터 입력, 네트워크로 출력 등등
자바는 데이터는 '스트림'을 통해 입출력된다.
이때 스트림은 단일 방향으로 연속적으로 흘러가는 데이터의 흐름을 말한다.
스트림에서 흐르는 데이터는 바이트 단위이다.
스트림은 프로그램으로 들어오는지, 프로그램에서 나가는지에 따라 종류가 달라진다.
프로그램으로 들어오는 흐름을 입력 스트림, 프로그램에서 나가는 흐름을 출력 스트림이라고 한다.
예를들어, A 프로그램에서 B 프로그램으로 데이터를 보낼 때,
A 프로그램에서는 출력 스트림을, B 프로그램에서는 입력 스트림을 사용해야 한다.
앞서 말했든, 스트림은 단방향이므로 입출력을 위해 각각 입력 스트림, 출력 스트림이 필요하다.
(하나의 스트림으로 입출력을 모두 할 수 없다.)
스트림은 운영체제에 의해 생성되는 가상의 연결 고리를 의미하며, 중간 매개자 역할을 한다고 이해할 수도 있다.
입출력 스트림의 종류
객체 지향 프로그램인 자바가 항상 그렇듯, 입출력 스트림 역시 '객체'로 이루어져있다.
자바는 이를 위해 java.io 패키지에서 크게 두 종류의 스트림 클래스를 제공하고 있다.
- 바이트 기반 스트림 : 바이트 단위의 이진 데이터를 다루므로 이미지나 동영상 파일을 읽고 출력할 때 유용하다.
- 문자 기반 스트림 : 2바이트 단위인 유니코드를 다루므로 문자 데이터를 읽고 출력할 때 유용하다.
* 바이트 기반 스트림은 모든 종류의 데이터를 주고 받을 수 있지만, 문자 기반 스트임은 문자 입출력에 특화되어있다.
어떤 스트림 클래스가 바이트 기반인지, 문자 기반인지
혹은 입력 스트림인지, 출력 스트림인지를 구별하려면, 접미사를 보면 된다.
모든 스트림 클래스는 최상위 클래스를 접미사로 갖기 때문이다.
InputStream, OutputStream은 바이트 기반 스트림의 최상위 클래스이고,
Reader와 Writer은 문자 기반 스트림의 최상의 클래스이다.
예를들어, FileWriter은 문자 기반 출력 스트림임을 알 수 있다.
이 최상위 클래스들은 모두 추상 클래스로, 스트림이 반드시 가져야 하는 메소드들을 정의하고 있다.
즉, 모든 스트림은 공통된 메소트가 있다.
특히 핵심 메소드인 read, wrtie 메소드는 다양하게 오버로딩(같은 이름, 다른 매개변수)되고 있다.
바이트 기반 스트림
▶ InputStream
바이트 기반 입력 스트림의 최상위 클래스로 추상 클래스다.
FileInputStream, BufferedInputStream, DataInputStream 등 모든 바이트 기반 입력 스트림은 InputStream을 상속하고 있다.
InputStream의 주요 메소드는 아래와 같다.
리턴 타입 | 메소드 | 설명 | ||
int | read( ) | 입력 스트림에서 1byte를 읽고 int로 리턴한다. int(4byte)의 마지막 byte에 값을 저장한다. |
||
int | read(byte[ ] b) | 입력 스트림에서 배열의 크기만큼 byte를 읽어 매개값으로 준 배열에 저장한다. 읽은 byte의 수를 int로 리턴한다. * 배열은 저장 장소, 읽는 곳은 입력 스트림! |
||
int | read(byte[ ] b, int off, int len) | read(byte[ ] b)와 동일한 기능을 하지만, 배열에서 저장을 시작할 곳과 저장할 바이트 수를 줄 수 있다. |
||
void | close( ) | 입력 스트림을 닫는다. |
▷ read() 메소드
바이트 기반 입력 스트림에서 read( )메소드는 세가지 방법으로 오버로딩되어있다.
이 메소드에서 리턴하는 int는 의미가 다르므로, 이를 주의해야 한다.
Ex. read( )는 읽은 byte 자체를 int에 넣어 리턴하지만, read(byte[ ] b)는 배열에 저장하고 읽은 byte의 수를 int로 리턴한다.
- int read( )
입력 스트림으로부터 1byte를 읽고 int 타입(4byte)으로 리턴한다.
따라서 리턴하는 int의 4byte 중 끝 1byte에만 데이터가 들어있다.
-> 이렇게 마지막 1byte에 들어있는 데이터는 출력 스트림의 write(int b) 메소드가 읽을 수 있다.
1byte씩만 읽을 수 있으므로, 입력 스트림이 5byte라면, 5번 실행되어야 한다.
더 이상 입력 스트림으로부터 바이트를 읽을 수 없다면 -1을 반환하는데,
이를 이용해 read( ) 메소드로 마지막 바이트까지 읽을 수 있다.
- int read(byte[ ] b)
입력 스트림으로부터 바이트를 읽고 매개값으로 준 바이트 배열에 저장한다.
한번에 배열의 길이 만큼의 바이트를 읽고 읽은 byte의 수를 int로 리턴한다.
마찬가지로 더 이상 입력스트림으로부터 바이트를 읽을 수 없다면 -1을 반환한다.
Ex. 입력 스트림에 5byte가 들어오고, 길이가 3인 배열로 읽으려 하면 순서대로 3과 2가 리턴된다.
- int read(byte[ ] b, int off, int len)
입력 스트림으로부터 바이트를 읽어 매개값으로 준 배열에 저장을 하고, 읽은 바이트 수를 리턴한다.
여기에 더하여, 배열에 저장할 때 어느 인덱스부터 몇개를 저장할지 정할 수 있다.
예를들어, 입력 스트림에 5byte가 들어올 때 read(b,1,3)메소드는 b[1]부터 3개의 byte를 저장하겠다는 뜻이 된다.
따라서 배열 b는 {0, ①, ②, ③, 0} 가 된다.
▶ OutputStream
바이트 기반 출력 스트림의 최상위 클래스이자 추상클래스이다.
하위 클래스로는 FileOutputStream, PrintOutputStream, BufferedOutputStream, DataOutputStream가 있다.
OutputStream의 주요 메소드는 아래와 같다.
리턴 타입 | 메소드 | 설명 | |||
void | write(int b) | 매개값 int(4byte)의 마지막 1byte를 출력 버퍼로 보낸다. | |||
void | write(byte[ ] b) | 매개값으로 준 배열을 출력 버퍼로 보낸다. | |||
void | write(byte[ ] b, int off, int len) | 매개값으로 준 배열을 출력 버퍼로 보내되, 어디서부터 시작해서 몇 byte를 보낼지 지정할 수 있다. |
|||
void | flush( ) | 출력 버퍼에 있는 바이트를 전부 출력 스트림으로 보낸다. | |||
void | close( ) | 출력 스트림을 닫는다. |
- void write(int b)
매개값으로 주어지는 int(4byte)의 끝 1byte를 읽어 출력 스트림으로 내보낸다.
(매개값으로 주는 int도 마지막 byte에만 데이터가 있으므로 마지막 1byte만 출력해도 괜찮다.)
Ex. 입력 스트림의 read 메소드에서 반환하는 int 타입 데이터는 마지막 1byte에만 데이터가 있다.
cf. 1byte는 8bit이므로 -2의 7승(-128)부터 2의 7승-1(127)까지는 int 그대로 1byte에 담길 수 있다.
- write(byte[] b)
매가값으로 주는 배열 안의 모든 바이트를 출력 버퍼로 내보낸다.
- write(byte[] b, int off, int len)
매개값으로 주는 바이트 배열을 출력 버퍼로 내보내되, b[off]부터 len개를 내보낸다.
- flush( )
출력 스트림은 출력할 바이트를 바로 보내는 것이 아니라, 내부 저장소인 버퍼에 우선 저장해놓고 출력한다.
따라서 우선 write메소드로 출력 버퍼에 저장해뒀다가
flush 메소드로 출력 버퍼에 잔류하는 모든 바이트를 출력 스트림으로 보낸다.
- close( )
OutputStream을 닫는다.
▷ void write(int) & int read( ) 실습
▷ void write(byte b[ ]) & int read(byte b[ ]) 실습
▷ void write(byte b[ ], int off, int len) & int read(byte b[ ], int off, int len) 실습
문자 기반 스트림
모든 문자 기반 스트림은 Reader 또는 Writer라는 최상위 클래스를 상속한다.
바이트 기반 스트림과 제공하는 메소드(read, write)가 동일한 방법으로 적용된다.
'문자' 기반 스트림인 만큼 입출력의 단위가 byte가 아니라 char(2byte) 라는 점만 다르다.
▶ 문자 기반 입력 스트림 Reader의 메소드
리턴 타입 | 메소드 | 설명 | |
int | read( ) | 입력 스트림에 있는 문자(2byte)를 읽고 int로 리턴한다. int(4byte) 끝의 2byte에 값을 저장한다. |
|
int | read(char[ ] cbuf) | 입력 스트림에 있는 문자를 배열에 저장한다. 저장한 byte 만큼을 int로 리턴한다. |
|
int | read(char[ ] cbuf, int off, int len) | 입력 스트림에 있는 문자를 배열에 저장하되, 저장할 위치와 길이를 설정할 수 있다. |
|
void | close | 입력 스트림을 닫는다. |
▶ 문자 기반 출력 스트림 Writer의 메소드
리턴 타입 | 메소드 | 설명 | |
void | write(int c) | 매개값으로 주어진 int에서 끝 2byte를 출력 버퍼로 보낸다. | |
void | write(char[ ] cbuf) | 매개값으로 주어진 char 배열을 모두 출력 버퍼로 보낸다. | |
void | write(char[ ] cbuf, int off, int len) | 매개값으로 주어진 char 배열을 모두 출력 버퍼로 보내되, 출력 시작 부분과 길이를 설정할 수 있다. |
|
void | write(String str) | 매개값으로 주어진 문자열을 모두 출력 버퍼로 내보낸다. cf. 문자열을 곧 '문자 배열'이므로 write(char[ ] cbuf)와 같은 역할을 한다. 하지만 ' '로 한글자씩 보내는게 아니라 " "로 묶어 보내므로 가독성은 더 좋다. |
|
void | write(String str, int off, int len) | 매개값으로 주어진 문자열을 출력 버퍼로 보내되, 출력 시작 부분과 길이를 설정할 수 있다. |
|
void | flush( ) | 버퍼에 잔류하는 모든 문자를 출력 스트림으로 보낸다. | |
void | close( ) | 출력 스트림을 닫는다. |
⭐총정리⭐
자바는 입출력을 스트림으로 처리한다.
스트림은 단방향이므로, 입력 스트림과 출력 스트림이 각각 필요하다.
자바는 객체지향 프로그램 답게 스트림도 객체로 만드는데, 이를 위해 java.io에서 두 종류의 스트림을 제공한다.
① 바이트 기반 스트림 InputStream, OutputStream ② 문자 기반 스트림 Reader, Writer
둘 다 read, write라는 메소드를 오버로딩해서 사용하고 원리는 동일하다.
하지만 읽는 단위가 다르다. 바이트 기반 - 1byte, 문자 기반 - 2byte(char)
int read( ) : 입력 스트림의 데이터를 읽고 int에 넣어 리턴
int read( 배열 ) : 입력 스트림의 데이터를 배열에 저장하고 읽은 바이트만큼 리턴
void write( int ) : int에 있는 값을 byte로 출력 스트림으로 보냄
void write( 배열 ) : 배열에 있는 모든 byte를 출력 스트림으로 보냄
Q. 입출력 버퍼는 왜 필요한걸까?
A. 속도 때문에
이사 할 때를 생각하면 쉽다.
하나씩 나르는 것보다 한꺼번에 나르는게 더 빠른 것과 같은 원리이다.
입력 버퍼의 경우, 버퍼를 사용하지 않으면 키보드에 입력하는 족족 시스템에 전달된다.
하지만, 이는 한번에 보내는 것보다 더 많은 포장, 더 많은 주고받는 과정을 거쳐야 하므로 훨씬 비효율적이다.
'JAVA > 혼공자' 카테고리의 다른 글
[혼공자] Chapter 14-3 입출력 관련 API (1) | 2022.08.22 |
---|---|
[혼공자] Chapter 14-2 보조 스트림 (1) | 2022.08.22 |
[혼공자] Chapter 13-2 LIFO와 FIFO 컬렉션 (1) | 2022.08.20 |
[혼공자] Chapter 13-1 컬렉션 프레임워크 (1) | 2022.08.19 |
[혼공자] Chapter 12-2 스레드 제어 (1) | 2022.08.17 |