-
Java 항해일지 - 13. I/O공부일기/Java 8 2021. 2. 28. 12:55
목표
자바의 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()을 이용해 모니터 화면에 출력하던가.
스트림 (Stream) / 버퍼 (Buffer) / 채널 (Channel) 기반의 I/O
- 스트림이란?
연속된 데이터의 흐름. 입출력 진행 시 다른 작업을 할 수 없는 블록킹 상태가 된다.
입출력을 변경하기 편하고, 동일한 프로그램 구조를 유지할 수 있다.
단방향으로 흐르기 때문에 입력과 출력을 동시에 할 수 없다.
- 버퍼란?
byte, char, int 등 기본 데이터 타입을 저장할 수 있는 저장소. 배열과 마찬가지로 제한된 크기에 순서대로 데이터를 저장한다.
데이터를 저장하기 위한 것이지만 실제 사용은 채널을 통해 데이터를 주고 받을 때 사용된다.
채널을 통해 소켓, 파일 등에 데이터를 전송하거나 읽어올 때 버퍼를 사용함으로써 불필요한 데이터의 양을 줄일 수 있고, 서버의 전체 처리량을 늘릴 수 있다.
- 채널이란?
데이터가 통과하는 쌍방향 통로. 버퍼와 데이터를 주고 받는다.
채널에는 소켓과 연결된 SocketChannel, 파일과 연결된 FileChannel, 파이프와 연결된 Pipe.SinkChannel 과 Pipe.SourceChannel 등이 존재하며, 서버소켓과 연결된 ServerSocketChannel 도 존재한다.
- IO vs NIO
IO 의 방식으로 각각의 스트림에서 read() 와 write() 가 호출이 되면 데이터가 입력 되고, 데이터가 출력되기전까지, 스레드는 블로킹(멈춤) 상태가 된다. 이렇게 되면 작업이 끝날때까지 기다려야 하며, 그 이전에는 해당 IO 스레드는 사용할 수 없게 되고, 인터럽트도 할 수 없다. 블로킹을 빠져나오려면 스트림을 닫는 방법 밖에 없다.
NIO(New I/O) 기존 IO의 단점을 보완하기 위해 java 4부터 추가된 패키지이다. 블로킹 상태에서는 Interrupt 를 이용하여 빠져나올 수 있다.
IO NIO 입출력 방식 스트림 방식 채널 방식 버퍼 방식 논버퍼 버퍼 비동기 방식 지원안함 지원 블로킹/ 논블로킹 방식 블로킹 방식만 지원
(동기)블로킹/ 논블로킹 모두 지원
(동기/ 비동기 모두 지원)스트림과 채널 (Stream vs Channel)
IO는 스트림(Stream) 기반.NIO는 채널(Channel) 기반이다.
넌버퍼와 버퍼 (non-buffer vs buffer)
IO에서는 출력 스트림이 1바이트를 쓰면 입력 스트림이 1바이트를 읽는데 이러한 시스템은 대체로 느리다.
따라서 이것보다는 버퍼(Buffer : 메모리 저장소)를 사용해서 복수 개의 바이트를 한꺼번에 입력받고 출력하는 것이 성능에 이점을 가진다.
그래서 IO는 버퍼를 제공해주는 보조 스트림인 BufferedInputStream, BufferedOutputStream을 연결해 사용하기도 한다.
NIO는 기본적으로 버퍼를 사용해서 입출력을 하기 때문에 IO보다 높은 성능을 가진다.
IO는 스트림으로부터 입력된 전체 데이터를 별도로 저장하지 않으면, 입력된 데이터의 위치를 이동해 가면서 자유롭게 이용할 수 없다.
NIO는 읽은 데이터를 무조건 버퍼에 저장한다. 따라서 버퍼 내에서 데이터의 위치 이동을 해가면서 필요한 부분만 읽고 쓸 수 있다.
블로킹과 넌블로킹 (Blocking vs non-blocking)
IO는 블로킹(Blocking) 된다.
NIO는 블로킹과 넌블로킹(non-blocking) 특징을 모두 가진다.
InputStream과 OutputStream
- InputStream
바이트 기반 입력 스트림의 최상위 추상 클래스이다.
모든 바이트 기반 입력 스트림은 이 클래스를 상속 받아서 만들어 진다.
버퍼, 파일, 네트워크 단에서 입력되는 데이터를 읽어오는 기능을 수행한다
아래는 InputStream의 메서드
- read() : 입력 스트림으로부터 1바이트를 읽어서 바이트를 리턴
- read(byte[] b) : 입력 스트림으로부터 읽은 바이트들을 매개값으로 주어진 바이트 배열 b에 저장하고 실제 읽은 바이트 수를 리턴
- read(byte[] b, int off, int len) : 입력 스트림으로부터 len 개의 바이트만큼 읽고, 매개 값으로 주어진 바이트 배열 b[off]부터 len개 까지 저장. 그리고 실제 읽은 바이트 수인 len개를 리턴한다. 만약 len 개를 다 읽지 못하면 실제로 읽은 바이트 수를 리턴한다.
- close() : 사용한 시스템 자원을 반납하고 입력 스트림을 닫는다.
- OutputStream
바이트 기반 출력 스트림의 최상위 추상 클래스이다.
모든 바이트 기반 출력 스트림은 이 클래스를 상속 받아서 만들어 진다.
버퍼, 파일, 네트워크 단으로 데이터를 내보내는 기능을 수행한다.
아래는 OutputStream의 메서드
- write(int b) : 출력 스트림으로부터 1바이트를 보낸다.(b의 끝 1바이트)
- read(byte[] b) : 출력 스트림으로부터 주어진 바이트 배열 b의 모든 바이트를 내보낸다.
- read(byte[] b, int off, int len) : 출력 스트림으로 주어진 바이트 배열 b[off]부터 len개 까지의 바이트를 보낸다.
- flush() : 버퍼에 잔류하는 모든 바이트를 출력한다.
- close() : 사용한 시스템 자원을 반납하고 출력 스트림을 닫는다.
Byte와 Character 스트림
- Byte Stream
- binary 데이터를 입출력하는 스트림
- 데이터는 1바이트 단위로 처리
- 이미지, 동영상 등을 송수신 할 때 주로 사용
- Character Stream
- text 데이터를 입출력하는 스트림
- 데이터는 2바이트 단위로 처리
- 일반적인 텍스트 및 JSON, HTML 등을 송수신할 때 주로 사용
- Java에선 한 문자를 의미하는 char형이 1바이트가 아닌 2바이트이기 때문에 바이트 기반의 스트림으로 2바이트 문자를 처리하기 어렵다. 그렇기 때문에 자바에선 문자 기반 스트림을 사용한다.
- 보조 스트림
- FilterInputStream 과 FilterOutputStream 을 상속받는 클래스들로 기본 스트림과 결합하여 특정 상황에서 보다 편리하게 사용할 수 있다.
- BufferedInputStream/BufferedOutputStream: 버퍼를 사용해 입출력 효율과 편의를 위해 사용
- BufferedReader/BufferedWriter: 라인단위의 입출력이 편리함
- InputStreamReader/OutputStreamReader: 바이트 스트림을 문자 스트림처럼 쓸 수 있도록하며 문자 인코딩 변환을 지원
- DataInputStream/DataOutputStream: 자바 원시자료형 데이터 처리에 적합
표준 스트림(System.in, System.out, System.err)
- 표준 입출력 스트림의 종류는 java.lang 패키지의 System 클래스 내부에 static 으로 선언되어 있으며 다음과 같다.
public final class System { public static final InputStream in; public static final PrintStream out; public static final PrintStream err; .... }
- System.out : 콘솔 화면에 문자열을 출력하기 위한 용도로 사용되는 출력 스트림이다.
- System.in : 키보드의 입력을 받아들이기 위해서 사용되는 입력 스트림이다.
- System.out 과 System.err : 둘 다 출력 스트림. err는 ㅈ버퍼링 지원을 하지 않는다.
파일 읽고 쓰기
텍스트 파일인 경우 문자 스트림 클래스들을 사용하면 되고, 바이너리 파일인 경우 바이트 스트림을 기본적으로 사용한다.
입출력 효율을 위해 Buffered 계열의 보조 스트림을 함께 사용하는 것이 좋다.
- 텍스트 파일인 경우
BufferedReader br = new BufferedReader(new FileReader("a.txt")); BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt")); String s; while ((s = br.readLine()) != null) { bw.write(s + "\n"); }
- 바이너리 파일인 경우
BufferedInputStream is = new BufferedInputStream(new FileInputStream("a.jpg")); BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream("b.jpg")); byte[] buffer = new byte[16384]; while (is.read(buffer) != -1) { os.write(buffer); }
참고.
자바의 정석. 기초편 . 남궁성www.notion.so/I-O-af9b3036338c43a8bf9fa6a521cda242
bingbingpa.github.io/java/whiteship-live-study-week13/
'공부일기 > Java 8' 카테고리의 다른 글
좋은 객체 지향 설계의 5가지 원칙(SOLID) (0) 2021.06.21 Java 8 - 8. 애노테이션의 변화 (0) 2020.12.26 Java 8 - 7. Callable과 Future (0) 2020.12.25 Java 8 - 6. Concurrent 프로그래밍과 Executors (0) 2020.12.24 Java 8 - 5. Date & Time (0) 2020.12.23