[자바/Java] I/O(Input/Output)이란?
resilient
·2021. 2. 22. 13:49
github.com/whiteship/live-study/issues/13
목표
자바의 Input과 Ontput에 대해 학습하세요.
학습할 것 (필수)
- 스트림 (Stream) / 버퍼 (Buffer) / 채널 (Channel) 기반의 I/O
- InputStream과 OutputStream
- Byte와 Character 스트림
- 표준 스트림 (System.in, System.out, System.err)
- 파일 읽고 쓰기
# I/O
먼저 I/O는 Input 과 Output의 약자로 입려과 출력, 간단히 줄여서 입출력이라고 한다.
입출력은 컴퓨터 내부 또는 외부의 장치와 프로그램간의 데이터를 주고받는 것을 말한다.
예를 들면 키보드로부터 데이터를 입력받는다든가 System.out.println() 을 이용해서 화면에 출력한다던가 하는 것이 예시이다.
# 스트림 , 버퍼, 채널 기반의 I/O
자바에서 입출력을 수행할때 데이터를 전달하려면 두 대상을 연결하고 데이터를 전송할 수 있는 무언가가 필요한데, 이것을 스트림 이라고 한다.
스트림이란 데이터를 운반하는데 사용되는 연결통로 같은것이다.
입력과 출력을 동시에 수행하려면 입력을 위한 입력스트림과 출력을 위한 출력스트림, 2개의 스트림이 필요하다.
스트림은 먼저 보낸 데이터를 먼저 받게 되어 있으며 중간에 건너뜀 없이 연속적으로 데이터를 주고받는다.
Queue와 같이 FIFO구조로 되어 있다고 생각하면 된다.
바이트기반 스트림 - InputStream, OutputStream
스트림은 바이트다위로 데이터를 전송하며 입출력 대상에 따라 다음과 같은 입출력스트림이 있다.
입력스트림 | 출력스트림 | 입출력대상 종류 |
FileInputStream | FileOutputStream | 파일 |
ByteArrayInputStream | ByteArrayOutputStream | 메모리(byte배열) |
PipedInputStream | PipedOutputStream | 프로세스 |
AudioInputStream | AudioOutputStream | 오디오장치 |
어떠한 대상에 대해 입력을 할 것인지, 출력을 할 것인지에 따라 해당 스트림을 선택해서 사용하면된다.
위는 모두 InputStream 또는 OuputStream의 자손들이고 각각 읽고 쓰는데 필요한 추상메소드를 자신에 맞게 구현해 놓았다.
InputStream | OuputStream |
abstract int read() | abstract void write(int b) |
int read(byte [] b) | void write (byte [] b) |
int read(byte {} b, int off, int len) | void write(byte {} b, int off, int len) |
위에 read()의 반환타입이 byte가 아니라 int 인 이유는 read()의 반환값 범위가 0~255와 -1이기 때문이다.
public abstract class InputStream{
...
// 입력스트림으로 부터 1byte를 읽어서 반환한다. 읽을 수 없으면 -1을 반환한다.
abstract int read();
// 입력스트림으로부터 len개의 byte를 읽어서 byte배열 b의 off위치부터 저장한다.
int read(byte[] b, int off, int len){
...
for(int i = off ; i < off + len ; i++){
// read()를 호출해서 데이터를 읽어서 배열을 채운다.
b[i] = (byte)read();
}
...
// 입력스트림으로부터 byte배열 b의 크기만큼 데이터를 읽어서 배열 b에 저장한다.
int read(byte[] b){
return read(b, 0, b.length);
}
}
...
}
# Buffer기반의 스트림
Buffer란?
byte,char,int 등 기본 데이터 타입을 저장할 수 있는 저장소로서 배열과 마찬가지로 제한된 크기에 순서대로 데이터를 저장한다. 버퍼는 데이터를 저장하기 위한 것이지만, 실제로 버퍼가 사용되는 것은 채널을 통해서 데이터를 주고 받을때 사용된다.
스트림의 기능을 보완하기 위한 보조스트림이 있다. 보조스트림은 실제 데이터를 주고받는 스트림이 아니기 때문에 데이터를 입출력할 수 있는 기능은 없지만, 스트림의 기능을 향상시키거나 새로운 기능을 추가할 수 있다.
그래서 스트림을 먼저 생성한 다음에 이를 이용해서 보조스트림을 생성해야한다.
//먼저 기반스트림을 생성한다.
FileInputStream fis = new FileInputStream("test.txt");
//기반스트림을 이용해서 Buffer스트림을 생성한다.
BufferedInputStream bis = new BufferedInputStream(fis);
bis.read();
보조스트림에는 버퍼를 이용하여 입출력 성능향상을 시킬 수 있는 BufferedInputStream, BufferedOutputStream이 있다.
Buffer 을 사용하면 좋은 이유
한 바이트씩 바로바로 보내는 것이 아니라 버퍼에 담았다가 한번에 모아서 보내는 방법이므로 시스템 콜의 횟수가 줄어들게 되고 성능상 이점이 생기게 된다.
OS 레벨에 있는 시스템 콜의 횟수 자체를 줄이기 때문에 성능이 빨라지는 것이다.
# Channel 기반의 스트림
채널은 데이터가 통과하는 쌍방향 통로이며 채널에서 데이터를 주고 받을 때 사용 되는 것이 버퍼이다.
채널에는 소켓과 연결된 SocketChannel 파일과 연결된 FileChannel 파이프와 연결된 Pipe.SinkChannel 과 Pipe.SourceChannel 등이 존재한다.
# InputStream과 OutputStream
InputStream과 OutputStream은 모든 바이트 기반 스트림의 조상이며 많은 메소드들이 있다.
InputStream관련 메소드
read() | 입력 스트림으로부터 1바이트를 읽어서 바이트를 리턴 |
read(byte[] b) | 입력 스트림으로부터 읽은 바이트들을 매개값으로 주어진 바이트 배열 b 에 저장하고 실제로 읽은 바이트 수를 리턴 |
read(byte[] b, int off, int len) | 입력 스트림으로부터 len 개의 바이트만큼 읽고 매개값으로 주어진 바이트 배열 b[off] 부터 len 개까지 저장. 그리고 실제로 읽은 바이트 수인 len 개를 리턴. 만약 len 개를 모두 읽지 못하면 실제로 읽은 바이트 수를 리턴 |
close() | 사용한 시스템 자원을 반납하고 입력 스트림 닫기 |
OutputStream관련 메소드
write(int b) | 출력 스트림으로부터 1바이트를 보낸다.(b 의 끝 1바이트 |
write(byte[] b) | 출력 스트림으로부터 주어진 바이트 배열 b의 모든 바이트를 보낸다. |
write(byte[ ] b, int off, int len) | 출력 스트림으로 주어진 바이트 배열 b[off] 부터 len 개까지의 바이트를 보낸다. |
flush() | 버퍼에 잔류하는 모든 바이트를 출력한다. |
close() | 사용한 시스템 자원을 반납하고 입력 스트림 닫기 |
# Byte와 Character 스트림
바이트기반이라 함은 입출력의 단위가 1byte라는 뜻이다. C언어와 달리 Java에서는 한 문자를 의미하는 char형이 2byte이기 때문에 바이트기반의 스트림으로 2byte인 문자를 처리하는 데는 어려움이 있다.
이 점을 보완하기 위해서 문자기반의 스트림이 제공된다. 문자데이터를 입출력할 때는 바이트 기반 스트림 대신 문자기반 스트림을 사용하면 된다.
- Byte Stream
- binary 데이터를 입출력하는 스트림
- 데이터는 1바이트 단위로 처리
- 이미지, 동영상 등을 송수신 할 때 주로 사용
- Character Stream
- text 데이터를 입출력하는 스트림
- 데이터는 2바이트 단위로 처리
- 일반적인 텍스트 및 JSON, HTML 등을 송수신할 때 주로 사용
- 보조 스트림
- FilterInputStream 과 FilterOutputStream 을 상속받는 클래스들로 기본 스트림과 결합하여 특정 상황에서 보다 편리하게 사용할 수 있다.
- BufferedInputStream/BufferedOutputStream: 버퍼를 사용해 입출력 효율과 편의를 위해 사용
- BufferedReader/BufferedWriter: 라인단위의 입출력이 편리함
- InputStreamReader/OutputStreamReader: 바이트 스트림을 문자 스트림처럼 쓸 수 있도록하며 문자 인코딩 변환을 지원
- DataInputStream/DataOutputStream: 자바 원시자료형 데이터 처리에 적합
# 표준 스트림 (System.in, System.out, System.err)
표준 입출력은 콘솔(console, 도스창) 을 통한 데이터 입력과 콘솔로의 데이터 출력을 의미한다.
자바에서는 표준 입출력을 위해 3가지 표준 스트림을 제공한다. 이 들은 개발자가 별도로 스트림을 생성하는 코드를 작성하지 않고도 사용이 가능하다.
System.in 은 콘솔로부터 데이터를 입력받는데 사용한다.
System.out 은 콘솔로 데이터를 출력하는데 사용한다.
System.err 은 콘솔로 데이터를 출력하는데 사용한다.
# Reference
- 자바의 정석(저자.남궁성)
- bingbingpa.github.io/java/whiteship-live-study-week13/
'Language > Java & Kotlin' 카테고리의 다른 글
[자바/Java] 람다식이란? (0) | 2021.03.06 |
---|---|
[자바/Java] 제네릭이란? (0) | 2021.02.27 |
[자바/Java] 애노테이션(annotation)이란? (0) | 2021.02.17 |
[자바/Java] 열거형(enum) 이란? (0) | 2021.01.30 |
[자바/Java] 예외처리란? (0) | 2021.01.15 |