본문 바로가기
Language/Java

[Java]Java 에서의 I/O(입출력)-Stream

by jaee_ 2021. 9. 11.

File과 Files

자바에서는 java.io 패키지에 File이라는 클래스가 존재한다. 이 패키지는 파일뿐만 아니라 파일의 경로(path)의 정보도 포함하는 클래스이다. 때문에 유닉스 계열의 파일에서 사용하는 몇몇 기능을 제대로 제공하지 못한다. 이러한 문제를 해결하기 위해 Java 7부터 Files라는 클래스가 등장했다. Files 클래스는 기존에 있던 File 클래스에 있던 메소드를 대체하여 제공하며 모든 메소드가 static으로 선언되어 있어 객체를 따로 생성하지 않아도 된다는 장점이 있다. 

 

I/O

I/O이란 Input과 Output의 약자로서 컴퓨터 내부 또는 외부의 장치와 프로그램간의 데이터를 주고받는 것을 말한다. 예를 들면, 키보드로부터 데이터를 입력받는다던가 System.out.println() 을 이용하여 화면에 출력하는 것을 입출력의 기본적인 예라고 할 수 있다. 

 

자바에서는 byte기반 데이터를 처리하기 위해서 stream이라는 클래스를 제공하여 사용하였다. byte가 아닌 char 기반의 문자열로 되어있는 파일은 Reader와 Writer를 사용한다.

 

또한,  Java 1.4부터는 빠른 I/O를 처리하기 위해 NIO(new Input Output)이 추가되며 이를 통해 stream기반이 아닌 buffer와 channel 기반의 데이터 처리가 가능해졌다. 

 

Stream

Stream이란, 데이터를 운반하는데 사용되는 연결 통로라고 생각하면 된다. 또한, 데이터가 들어온 순서대로 흘러다니는 단방향 통로이다. 입구와 출구가 존재하며 입구를 InputSteam, 출구를 OutputStream이라고 한다. 그렇기 때문에 입력과 출력을 동시에 하려면 InputStream, OutputStream 총 두개의 스트림이 필요하다. 

 

Stream을 통해 데이터는 기본적으로 byte 또는 byte[] 형태로 흐르며 동기적, blocking 방식으로 동작한다. 이말은 데이터를 읽거나 쓰기 위해 스트림에 요청을 하면 해당요청이 끝날때까지 다른작업을 하지 못하고 무한정 기다린다. 때문에 Stream에서는 Closeable 인터페이스를 구현하고 있다. Closeable에는 close() 메소드를 제공하며 이를 이용하여 모든 stream처리가 끝나면 close() 메소드를 사용하여 닫는 작업을 반드시 해주어야 한다. 이를 해주지 않으면 프로세스가 종료될때까지 stream은 열려있는 상태가 지속되기 때문에 메모리 누수가 발생한다. 

 

또한, OutputStream에는 flush()라는 메소드가 존재하는데 이 메소드를 사용하면 스트림의 버퍼에 있는 모든 내용을 출력소스에 출력한다. 

 

byte기반 스트림 - InputStream , OutputStream

위의 stream에서도 설명했지만 InputStream과 OutputStream은 바이트 기반으로 데이터를 전송하며 입출력 대상에 따라 아래와 같은 입출력스트림이 따로 존재한다. 

 

입력스트림 출력스트림 입출력 대상의 종류
FileInputStream FileOutputStream File(파일)
ByteArrayInputStream ByteArrayOutputStream 메모리(byte 배열)
PipedInputStream PipedOutputStream 프로세스 (프로세스간의 통신)
AudioInputStream AudioOutputstream 오디오 장치

 

위의 표와 같이 여러종류의 입출력 스트림이 있으며, 입출력할 대상에 따라 해당 스트림을 선택해서 사용하면 된다. 예를들어, 어떤 파일의 내용을 읽고자 하면 FileOutputStream을 사용하면 된다. 위의 표는 모두 InputStream, OutputStream의 자손들이며 각각 읽고 쓰는데 필요한 추상메소드를 자신에 맞게 구현해 놓은 것이다. 

 

보조스트림

스트림의 기능을 보완하기 위한 보조스트림이 존재를 하는데 실제로 데이터를 주고받는 스트림은 아니기 때문에 데이터를 입출력할 수 있는 기능은 없지만, 스트림의 기능을 향상시키거나 새로운 기능을 추가할 수 있다. 그래서 보조스트림만으로는 입출력을 처리할 수 없고, 스트림을 먼저 생성한 다음에 이를 이용하여 보조스트림을 생성해야 한다. 

// 1. 기반스트림 생성
FileInputStream fileStream = new FileInputStream("text.txt");

// 2. 기반 스트림을 이용해서 보조스트림을 생성한다. 
BufferedInputStream bis = new BufferedInputStream(fileStream);

// 3. 보조스트림인 BufferedInputStream 으로부터 데이터를 읽는다. 
bis.read();

위의 코드처럼 먼저 기반스트림을 생성하고 기반스트림을 이용하여 보조스트림을 생성하며 보조스트림을 이용하여 read() 메소드를 수행한다. 버퍼를 사용한 입출력과 사용하지 않은 입출력간의 성능차이는 상당하기 때문에 대부분 버퍼를 이용한 보조스트림을 사용한다. 

 

문자기반 스트림

지금까지 알아본 스트림은 모두 byte기반 스트림이었다. 바이트 기반은 입출력 단위가 1byte라는 뜻이다. Java에서는 한문자를 의미하는 char형이 1byte가 아니라 2byte이다. 그렇기 때문에 바이트 기반의 스트림으로는 2byte인 문자를 처리하기에 어려움이 있다. 이를 해결하기 위해 문자기반의 스트림이 제공된다. 문자데이터를 입출력할 때는 문자기반 스트림을 사용하도록 하자. 문자기반 스트림은 입력은 Reader , 출력은 Writer로 제공이 된다. 

 

바이트기반 스트림 문자기반 스트림
FileInputStream
FileOutputStream
FileReader
FileWriter
ByteArrayInputStream
ByteArrayOutputStream
CharArrayReader
CharArrayWriter
PipedInputStream
PipedOutputStream
PipedReader
PipedWriter
AudioInputStream
AudioOutputstream
AudioReader
AudioWriter

 

위의 표를 보면 알 수 있듯이 byte기반의 스트림인 InputStream 은 Reader로 OutputStream은 Writer로만 변경해주면 문자기반 스트림을 만들 수 있다. 보조 스트림 역시 InputStream ,OutputStream를 Reader, Writer로 변경해주면 사용 가능하다. 

 


참고

https://javanitto.tistory.com/11?category=927180 

자바의 신

자바의 정석

댓글