Develop/Java+Kotlin

[Java] 입출력(I/O)과 버퍼(Buffer)

연로그 2022. 9. 5. 19:53
반응형

서론

 

 ByteArrayOutputStream과 BufferedOutputStream의 차이점에 대한 질문을 받았다. 내가 Java의 입출력 시스템과 buffer에 대한 이해도가 떨어진다는 상태를 자각하게 되었다. 이를 공부하는 내용을 작성하려고 한다.

 

목차

  1. stream이란?
  2. InputStream & OutputStream
  3. InputStreamReader & OutputStreamWriter
  4. buffer란?
  5. BufferedInputStream & BufferedOutputStream 
  6. BufferedReader & BufferedWriter

 


Java의 입출력

 java에서의 입출력은 Stream을 통해 이루어진다.

 

1. stream이란?

  • 데이터를 운반하는데 사용되는 연결 통로
  • stream 이름의 어원: 데이터의 흐름이 물의 흐름 같이 단방향 통신만 가능
    (= 하나의 stream은 입, 출력 동시에 처리 불가능)
  • 먼저 보낸 데이터를 먼저 받는 FIFO 구조

 

img:https://medium.com/@nilasini/java-nio-non-blocking-io-vs-io-1731caa910a2

  • 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란?

img:http://www.tcpschool.com/c/c_io_console

  • buffer; 완충 장치; 완화하다
  • 데이터를 전송하는 동안 일시적으로 해당 데이터를 보관하는 메모리의 임시 공간
  • 데이터를 하나씩이 아닌 묶어서 한 번에 전달해 전송 시간의 단축
  • 버퍼를 쓰는게 항상 좋은 것은 아님
    (ex: 빠른 응답이 필요한 게임 등)

 

5. BufferedInputStream & BufferedOutputStream 

 InputStream / OutputStream의 성능 향상을 위해 buffer가 적용되었다. InputStream과 마찬가지로 byte 단위로 읽는다.

 

6. BufferedReader & BufferedWriter

 해당 클래스는 InputStream을 문자 단위로 처리하기 어려운 점을 보완하기 위해 나온 InputStreamReader같은 존재이다. BufferedInputStream과 BufferedOutputStream을 문자 단위로 처리하기 편하도록 만들어졌으며 사용 방법이나 메서드 등에 대한 설명은 이 링크에서 한다.

 

[Java] BufferedReader, BufferedWriter

프로그래머스 코테는 파라미터를 알아서 넘겨 받도록 되어있는데.. 백준은 직접 입력 받아야했다. Scanner와 sysout을 쓰다가 속도가 너무 느리게 나와서 BufferedReader/Writer에 대해 알아보려고 한다.

yeonyeon.tistory.com

 

 


참고

반응형