서론
ByteArrayOutputStream과 BufferedOutputStream의 차이점에 대한 질문을 받았다. 내가 Java의 입출력 시스템과 buffer에 대한 이해도가 떨어진다는 상태를 자각하게 되었다. 이를 공부하는 내용을 작성하려고 한다.
목차
- stream이란?
- InputStream & OutputStream
- InputStreamReader & OutputStreamWriter
- buffer란?
- BufferedInputStream & BufferedOutputStream
- BufferedReader & BufferedWriter
Java의 입출력
java에서의 입출력은 Stream을 통해 이루어진다.
1. stream이란?
- 데이터를 운반하는데 사용되는 연결 통로
- stream 이름의 어원: 데이터의 흐름이 물의 흐름 같이 단방향 통신만 가능
(= 하나의 stream은 입, 출력 동시에 처리 불가능) - 먼저 보낸 데이터를 먼저 받는 FIFO 구조
- Source: 데이터 시작점
- Sink: 데이터 종착점
- Stream: source와 sink를 연결한 것
- input stream: 입력을 위한 스트림
- output stream: 출력을 위한 스트림
2. InputStream & OutputStream
InputStream과 OutputStream은 abstract class이다. 이를 상속받는 클래스들은 입출력 대상에 따라 다양하게 존재한다. (아래 표 이외에도 다양한 I/O Stream이 존재) 해당 클래스의 중요한 점은 입출력 단위가 byte라는 것이다.
입출력 대상의 종류 | 입력 스트림 | 출력 스트림 |
파일 | FileInputStream | FileOutputStream |
메모리 (byte[]) | ByteArrayInputStream | ByteArrayOutputStream |
프로세스 | PipedInputStream | PipedOutputStream |
주요 메서드로는 값을 읽고 쓰는 read와 write가 있다.
InputStream | OutputStream |
abstract int read() | abstract int write() |
int read(byte[] b) | int write(byte[] b) |
int read(byte[] b, int off, int len) | int write(byte[] b, int off, int len) |
추가적으로 OuputStream에는 close()와 flush()이라는 메서드가 존재한다. 둘 다 리소스를 해제해주는 역할을 하지만 close()는 stream을 영구적으로 닫아 재사용할 수 없는 상태로 만든다.
예제 코드
테스트 코드를 잘 모르는 사람들은 assertThat 없이 System.out.println()을 이용해 bytes와 outputStream.toByteArray()를 찍어보기를 바란다.
@Test
void test() throws IOException {
byte[] bytes = {0, 1, 2, 3, 4, 5};
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); // bytes를 inputStream에 저장
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(bytes.length);
int data = 0;
while ((data = inputStream.read()) != -1) { // 읽어올 수 있는 값이 존재하지 않으면 -1이 반환됨
outputStream.write(data); // 읽어온 byte를 outputStream에 쓰기
}
assertThat(bytes).isEqualTo(outputStream.toByteArray()); // 동일한지 검사
}
3. InputStreamReader & OutputStreamWriter
InputStream과 OutputStream으로는 문자를 처리하기 어려움을 겪을 수 있다. 이를 보완하기 위해 등장한 것이 Reader와 Writer인데 먼저 어떤 문제점이 있는지 살펴보자.
- Java에서는 char형이 2byte이다.
(Input/OutputStream은 1 byte 단위로 읽어온다는 점을 기억하자.) - 인코딩 정보가 필요하다.
InputStreamReader의 생성자를 살펴보았다. 내가 인코딩과 관련된 정보를 주지 않아도 default charset을 가져와 이용하는 모습을 확인할 수 있다.
public InputStreamReader(InputStream in) {
super(in);
sd = StreamDecoder.forInputStreamReader(in, this,
Charset.defaultCharset()); // ## check lock object
}
4. buffer란?
- buffer; 완충 장치; 완화하다
- 데이터를 전송하는 동안 일시적으로 해당 데이터를 보관하는 메모리의 임시 공간
- 데이터를 하나씩이 아닌 묶어서 한 번에 전달해 전송 시간의 단축
- 버퍼를 쓰는게 항상 좋은 것은 아님
(ex: 빠른 응답이 필요한 게임 등)
5. BufferedInputStream & BufferedOutputStream
InputStream / OutputStream의 성능 향상을 위해 buffer가 적용되었다. InputStream과 마찬가지로 byte 단위로 읽는다.
6. BufferedReader & BufferedWriter
해당 클래스는 InputStream을 문자 단위로 처리하기 어려운 점을 보완하기 위해 나온 InputStreamReader같은 존재이다. BufferedInputStream과 BufferedOutputStream을 문자 단위로 처리하기 편하도록 만들어졌으며 사용 방법이나 메서드 등에 대한 설명은 이 링크에서 한다.
참고
- Java의 정석 - Chapter 14 입출력(I/O) / 남궁성 지음
- https://docs.oracle.com/javase/9/docs/api/java/io/InputStream.html
- https://docs.oracle.com/javase/9/docs/api/java/io/OutputStream.html
- https://stackoverflow.com/questions/22436289/when-to-use-buffer-and-what-for
- https://stackoverflow.com/questions/648309/what-does-it-mean-by-buffer
- http://www.tcpschool.com/c/c_io_console
- https://stackoverflow.com/questions/9272585/difference-between-flush-and-close-function-in-case-of-filewriter-in-java
'Develop > Java' 카테고리의 다른 글
[Java] Thread Pool 이해하기 (0) | 2022.09.12 |
---|---|
[Java] Thread 이해하기 (0) | 2022.09.08 |
[Java] 날짜, 시간 API (0) | 2022.08.30 |
[Java] java.lang.ClassNotFoundException: javax.xml.bind.JAXBException 에러 (0) | 2022.06.20 |
[Java] Duplicate key XXX 에러 (0) | 2022.05.18 |