Home [Java] 입출력 스트림 (InputStream,OutputStream)
Post
Cancel

[Java] 입출력 스트림 (InputStream,OutputStream)

3월 4주차 스터디 발표 자료📖
오늘은 입출력 스트림의 개념에 대해 알아보겠습니다!!

입출력 스트림 (I/O Stream)


  • 자바에서는 파일이나 콘솔의 입력을 직접 다루지 않고 스트림(Stream)을 통해 다룬다.
    즉, 입출력 스트림을 통해 입출력 장치와 자바 응용프로그램을 연결하여 데이터를 읽고 쓴다.
  • 🤔 입출력 스트림에서의 스트림은 무엇을 의미할까?
    • 자바 8에서 등장하는 함수형 프로그래밍 방식의 반복자와는 다른 의미이다.
    • 스트림이란 실제 입력이나 출력이 표현된 데이터의 연속적인 흐름이며 단방향성을 가진다.
    • 스트림은 단방향성을 띄기때문에 입력과 출력을 동시에 처리할 수 없다. 따라서 입력 스트림과 출력 스트림으로 구분하여 사용한다.

입출력 스트림 종류

사진 출처 : https://javaconceptoftheday.com/byte-stream-vs-character-stream-in-java/

입출력 스트림

  • 자바의 입출력 스트림은 크게 바이트 기반 스트림(Byte Stream)과 문자 기반 스트림(Character)으로 구분할 수 있다.
  • 바이트 스트림
    • 바이트 기반 스트림은 문자, 그림, 영상 등 다양한 형태의 데이터를 주고 받을 수 있다.
    • 자바에서 스트림은 기본적으로 바이트 단위로 데이터를 전송한다.
    • 바이트 스트림의 처리 단위는 1byte이다.
  • 문자 스트림
    • 문자 기반 스트림은 오직 문자만 주고 받을 수 있도록 설계되었다.
    • 16bit 유니코드 문자를 주고받는다.
    • 문자 스트림의 처리 단위는 2byte이다.

바이트 기반 스트림


  • 바이트 스트림은 InputStream 클래스와 OutputStream 클래스로 구성되어 있다.

바이트 스트림의 종류

입력 스트림출력 스트림입출력 대상
FileInputStreamFileOuputStream파일
ByteArrayInputStreamByteArrayOutputStream메모리(byte 배열)
PipedInputStreamPipedOutputStream프로세스
AudioInputStreamPipedOutputStream오디오

바이트 스트림의 보조 스트림

  • 보조 스트림?
    • 보조 스트림은 기존 스트림의 기능을 향상시키거나 추가 및 보완하기 위해 제공된다.
    • 스트림을 먼저 생성한 후 보조 스트림을 생성해야한다.
입력 스트림출력 스트림입출력 대상
BufferedInputStreamBufferedOutputStream버퍼를 이용한 입출력 성능 향상
DataInputStreamDataOutputStream기본형 단위로 데이터를 처리하는 기능
ObjectInputStreamObjectOutputStream데이터를 객체단위로 읽고 쓰는데 사용 (직렬화/역직렬화)
네트워크 상에서 Java 객체를 전송할 때 사용
FilterInputStreamFilterOutputStream필터를 이용한 입출력 처리
1
2
3
FileInputStream fileInputStream = new FileInputStream("test.txt");
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
bufferedInputStream.read();

InputStream

  • Input Stream 클래스는 데이터 소스로부터 byte 단위로 데이터를 읽어올 때 사용된다.
  • Input Stream은 모든 입력 스트림 클래스의 조상 클래스이며 다양한 메서드를 제공한다.
InputStream.java
  • InputStream이 제공하는 메서드(일부)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    
      public static InputStream nullInputStream() {
              return new InputStream() {
                  private volatile boolean closed;
    
                  private void ensureOpen() throws IOException {
                      if (closed) {
                          throw new IOException("Stream closed");
                      }
                  }
    
                  @Override
                  public int available () throws IOException {
                      ensureOpen();
                      return 0;
                  }
    
                  @Override
                  public int read() throws IOException {
                      ensureOpen();
                      return -1;
                  }
    
                  @Override
                  public int read(byte[] b, int off, int len) throws IOException {
                      Objects.checkFromIndexSize(off, len, b.length);
                      if (len == 0) {
                          return 0;
                      }
                      ensureOpen();
                      return -1;
                  }
    
                  @Override
                  public byte[] readAllBytes() throws IOException {
                      ensureOpen();
                      return new byte[0];
                  }
    
                  @Override
                  public int readNBytes(byte[] b, int off, int len)
                      throws IOException {
                      Objects.checkFromIndexSize(off, len, b.length);
                      ensureOpen();
                      return 0;
                  }
    
                  @Override
                  public byte[] readNBytes(int len) throws IOException {
                      if (len < 0) {
                          throw new IllegalArgumentException("len < 0");
                      }
                      ensureOpen();
                      return new byte[0];
                  }
    
                  @Override
                  public long skip(long n) throws IOException {
                      ensureOpen();
                      return 0L;
                  }
    
                  @Override
                  public long transferTo(OutputStream out) throws IOException {
                      Objects.requireNonNull(out);
                      ensureOpen();
                      return 0L;
                  }
    
                  @Override
                  public void close() throws IOException {
                      closed = true;
                  }
              };
          }
    

InputStream이 제공하는 메서드

  1. abstract int read()
    • 입력 스트림으로부터 1바이트를 읽어서 int 값으로 반환한다.
  2. int read(byte[] b, int off, int len)
    • 입력 스트림으로부터 len 만큼 읽어서 byte[] b의 off 위치에 저장하고 읽은 바이트 수를 반환한다.
  3. int read(byte[] b)
    • 입력 스트림으로부터 인자로 주어진 바이트 배열의 크기만큼 데이터를 읽어 저장하고 읽은 바이트 수를 반환한다.
  4. int available()
    • 현재 입력 스트림에서 읽을 수 있는 바이트 수를 반환한다.
  5. void close()
    • 입력 스트림과 관련된 자원을 반납하고 종료한다.
  6. void mark(int readlimit)
    • 입력 스트림의 현재 위치를 표시한다.
  7. void reset()
    • mark() 메서드가 마지막으로 호출된 시점으로 이동한다.
  8. boolean markSupported()
    • 입력스트림이 mark() , reset() 메서드를 지원하는지 확인한다.
  9. long skip(long n)
    • 입력 스트림에서 n 바이트의 데이터를 건너뛰고 실제로 건너뛴 바이트 수를 반환한다.

OutputStream

  • Output Stream 클래스는 데이터 소스로부터 byte 단위로 데이터를 특정 대상으로 출력하는 데 사용된다.
  • OutputStream은 모든 출력 스트림 클래스의 조상 클래스이며 다양한 메서드를 제공한다.
OutputStream.java
  • OutputStream이 제공하는 메서드

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    
      public abstract class OutputStream implements Closeable, Flushable {
          public static OutputStream nullOutputStream() {
              return new OutputStream() {
                  private volatile boolean closed;
    
                  private void ensureOpen() throws IOException {
                      if (closed) {
                          throw new IOException("Stream closed");
                      }
                  }
    
                  @Override
                  public void write(int b) throws IOException {
                      ensureOpen();
                  }
    
                  @Override
                  public void write(byte b[], int off, int len) throws IOException {
                      Objects.checkFromIndexSize(off, len, b.length);
                      ensureOpen();
                  }
    
                  @Override
                  public void close() {
                      closed = true;
                  }
              };
          }
    
           
          public abstract void write(int b) throws IOException;
    
          public void write(byte b[]) throws IOException {
              write(b, 0, b.length);
          }
    
          public void write(byte b[], int off, int len) throws IOException {
              Objects.checkFromIndexSize(off, len, b.length);
              // len == 0 condition implicitly handled by loop bounds
              for (int i = 0 ; i < len ; i++) {
                  write(b[off + i]);
              }
          }
    
          public void flush() throws IOException {
          }
    
          public void close() throws IOException {
          }
      }
    

OutputStream이 제공하는 메서드

  1. write(int b)
    • b의 1바이트 만큼만 OutputStream에 쓴다(=출력한다).
  2. write(byte[] b)
    • 인자로 주어진 바이트 배열의 모든 바이트를 OutputStream에 쓴다.
  3. void write(byte[] b, int off, int len)
    • 인자로 주어진 바이트 배열의 off 위치부터 len 크기 만큼의 바이트를 outputStream에 쓴다.
  4. close()
    • 출력 스트림과 관련된 자원을 반납하고 종료한다.
  5. flush()
    • OutputStream 내부 버퍼에 남아있는 데이터를 모두 출력한다.
    • Stream의 내부 Buffer란 출력데이터를 저장하기 위한 일시적인 메모리 영역이다.
    • OutputStream은 기본적으로 내부에 Buffer를 가지고있지 않아 OutputStream에 쓰면 바로 출력 대상에 출력이 된다.
  • 🤔 OutputStream이 내부에 Buffer를 가지고 있지 않다면 언제 flush 메서드를 사용하는걸까?
    • BufferedOutputStream 같이 내부에 Buffer를 가질 수 있는 하위 클래스를 사용하면 내부 Buffer를 사용할 수 있다.
    • 내부 Buffer를 사용하면 Buffer가 꽉찰 때나 프로그램이 종료될 때 데이터를 한번에 출력하도록 하여 출력 횟수를 줄일 수 있다 → 성능 개선

문자 기반 스트림


  • 바이트 기반의 스트림은 단순히 1 byte 단위로 데이터를 처리하기 때문에 2 byte 문자를 다루는 데 한계가 있다. 이러한 한계를 보완하기 위해 등장한 것이 문자 기반 스트림이다.
  • 문자 기반 스트림은 내부적으로 유니코드를 바이트 배열로 변환하고 이를 다시 지정된 인코딩 방식으로 변환하여 입출력한다.
  • 바이트 스트림은 byte[]를 사용한다면 문자 기반 스트림은 char[]를 사용한다.

문자 기반 스트림의 종류

ReaderWriter입출력 대상
FileReaderFileWriter파일
CharArrayReaderCharArrayWriter메모리(byte 배열)
PipedReaderPipedWriter프로세스
StringReaderStringWriter문자열

문자 기반 스트림의 보조 스트림

ReaderWriter입출력 대상
BufferedReaderBufferedWriter버퍼를 이용해 입출력 성능 향상
InputStreamReaderOutputStreamWriter바이트 기반 스트림을 문자 기반 스트림으로 변경
바이트 기반 스트림 데이터를 지정된 인코딩 문자 데이터로 변환
  • Scanner와 BufferReader
    • 우리는 흔히 문자를 입력받을 때 Scanner나 BufferReader를 사용한다. 이 둘의 차이는 뭘까?
    • Scanner는 BufferReader에 비해 속도가 느리다.
      • Scanner의 경우는 1KB의 버퍼를 갖고 BufferReader는 8KB의 버퍼를 갖는다. 따라서 문자열을 크기가 커지면 BufferReader의 출력 횟수가 더 적어지기 때문에 속도가 더 빠르다.

Reader

  • 문자 기반 입력 스트림의 최상위 클래스이며 모든 문자 기반 입력 스트림은 Reader 클래스를 상속한다.
  • 다양한 입력 소스를 처리할 수 있다.
  • Reader가 제공하는 메서드는 InputStream에서 제공하는 메서드와 char[] 배열을 사용한다는 것을 제외하면 다르지 않아 생략한다!

Writer

  • 문자 기반 출력 스트림의 최상위 클래스이며 모든 문자 기반 출력 스트림은 Writer 클래스를 상속한다.
  • 다양한 출력 대상을 처리할 수 있다.

Writer가 제공하는 주요 메서드

  • Writer가 제공하는 메서드는 Reader와 마찬가지로 OutputStream에서 제공하는 메서드와 char[] 배열을 사용한다는 것을 제외하면 크게 다르지 않다. 따라서 OutputStream의 메서드와 다른 일부 메서드만 정리한다.
  1. Writer append(char c)
    • Writer에 문자를 추가한다.
  2. Writer append(CharSequence csq)
    • Writer에 CharSequence를 추가한다.
  3. Writer append(CharSequence csq, int start, int end)
    • Writer에 CharSequence의 start부터 end의 문자까지 추가한다.
  • 🤔 append의 반환값은 왜 Writer일까?
    • Writer 객체를 반환하면 동일한 문자 스트림에 대해 여러번 append() 메소드를 호출하여 여러 문자열을 연속적으로 추가할 수 있다 (메소드 체이닝)
      가독성과 효율성이 높아진다.
    1
    2
    3
    4
    5
    6
    7
    
      // 메소드 체이닝
      writer.append("Hello, ").append("world!").append("\n");
    
      // 메소드 체이닝 x
      writer.append("Hello, ");
      writer.append("world!");
      writer.append("\n");
    
This post is licensed under CC BY 4.0 by the author.