목표
- 자바의 Input과 Ontput에 대해 학습하세요
학습할 것 (필수)
- 스트림 (Stream) / 버퍼 (Buffer) / 채널 (Channel) 기반의 I/O
- InputStream과 OutputStream
- Byte와 Character 스트림
- 표준 스트림 (System.in, System.out, System.err)
- 파일 읽고 쓰기
스트림 (Stream) / 버퍼 (Buffer) / 채널 (Channel) 기반의 I/O
스트림(Stream)
public class InputStreamExample {
public static void main(String[] args) {
try (InputStream stream = new FileInputStream("hello.txt")) {
int data;
while ((data = stream.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public class ReaderExample {
public static void main(String[] args) {
try (FileReader reader = new FileReader("hello.txt")) {
int data;
while ((data = reader.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
- java.io 패키지에서 제공되는 가장 기본적인 입출력 방식
- 바이트 스트림(InputStream, OutputStream) = 바이트 데이터를 입출력하는데 사용됨
- 문자스트림(Reader, Writer) = 문자 데이터를 입출력하는데 사용됨
- 단순하고 직관적인 입출력 방식이지만 성능면에서 큰 파일을 처리할 때는 버퍼링이 필요할 수 있음
버퍼(Buffer)
public class BufferedInputStreamExample {
public static void main(String[] args) {
//BufferedInputStream의 기본 버퍼 사이즈 = 8192
try (BufferedInputStream stream = new BufferedInputStream(new FileInputStream("hello.txt"))) {
int data;
while ((data = stream.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public class BufferedReaderExample {
public static void main(String[] args) {
try (BufferedReader reader = new BufferedReader(new FileReader("hello.txt"))) {
int data;
while ((data = reader.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
- BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter와 같은 버퍼 기반의 입출력 방식
- 버퍼는 데이터를 임시로 저장하는 메모리 공간으로 입출력 성능을 향상시키기 위해 사용됨
- 데이터 입출력할 때 매번 파일 시스템이나 네트워크와의 직접적인 통신을 하지 않고 버퍼에 데이터를 모아 한번에 처리함 -> 입출력 속도 향상
채널(Channel)
public class ChannelExample {
public static void main(String[] args) {
try (FileInputStream stream = new FileInputStream("hello.txt")) {
FileChannel channel = stream.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int data;
while ((data = channel.read(buffer)) != -1) {
buffer.flip();
System.out.print(new String(buffer.array(), 0, data));
buffer.clear();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
- java.nio 패키지에서 제공되는 입출력 방식으로 NIO(New I/O)라고도 불림
- 기존의 Stream IO 보다 더 빠르고 확장성이 높은 입출력 방식
- Channel과 Buffer를 사용하여 입출력 작업을 수행함
- Channel은 양방향으로 데이터를 전송할 수 있는 채널을 나타내며 Buffer는 데이터를 임시로 저장하는 메모리 공간임
- 비동기 입출력(Asynchronous IO)을 지원하여 블로킹 방식의 IO보다 더욱 효율적으로 입출력 작업을 처리할 수 있음
- NIO를 사용하는 것은 주로 네트워크 프로그래밍과 대용량 파일 처리에 적합함
InputStream과 OutputStream
InputStream
- java.io.InputStream 클래스는 바이트 기반 데이터를 읽어들이기 위한 추상 클래스임
- 주로 파일, 네트워크 연결, 메모리 버퍼 등과 같은 데이터소스로부터 바이트 데이터를 읽어옴
- InputStream의 하위 클래스들은 다양한 데이터소스로부터 데이터를 읽기 위한 메서드들을 구현함
- FileInputStream, ByteArrayInputStream, SocketInputStream 등이 있음
OutputStream
public class OutputStreamExample {
public static void main(String[] args) {
try (OutputStream stream = new FileOutputStream("bye.txt")) {
String content = "bye world!!!";
byte[] data = content.getBytes();
stream.write(data);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
- java.io.OutputStream 클래스는 바이트 기반 데이터를 출력하기 위한 추상 클래스임
- 주로 파일, 네트워크 연결, 메모리 버퍼 등과 같은 데이터 목적지로 바이트 데이터를 사용함
- OutputStream의 하위 클래스들은 다양한 데이터소스로부터 데이터를 쓰기 위한 메서드들을 구현함
- FileOutputStream, ByteArrayOutputStream, SocketOutputStream 등이 있음
Byte와 Character 스트림
Byte 스트림(Byte Stremas)
- java.io 패키지에 속하나 InputStream과 OutputStream 클래스를 통해 구현됨
- 바이트 기반 데이터를 다루기 때문에 이미지, 음성 등의 이진 데이터를 처리하는데 적합함
- InputStream은 바이트 단위로 데이터를 읽기 위한 추상클래스이며 FileInputStream, ByteArrayInputStream, SocketInputStream 등이 있음
- OutputStream은 바이트 단위로 데이터를 쓰기 위한 추상클래스이며 FileOutputStream, ByteArrayOutputStream, SocketOutputStream 등이 있음
Character 스트림(Character Stremas)
- java.io 패키지에 속한 Reader와 Writer 클래스를 통해 구현됨
- 문자 기반 데이터를 다루기 때문에 텍스트 파일 등 문자 데이터를 처리하는데 적합함
- 문자 인코딩과 관련하여 훨씬 더 유연하며 특히 Unicode 문자를 처리하는데 강력한 기능을 제공함
- Reader는 문자 단위로 데이터를 읽기 위한 추상 클래스이며 FileReader, StringReader, InputStreamReader 등이 있음
- Writer는 문자 단위로 데이터를 쓰기 위한 추상 클래스이며, FileWriter, StringWriter, OutputStreamWriter 등이 있음
표준 스트림 (System.in, System.out, System.err)
public class StandardStreamExample {
public static void main(String[] args) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
System.out.print("이름을 입력하세요 : ");
String name = reader.readLine();
System.out.println("입려한 이름은 : " + name);
throw new IllegalStateException();
} catch (Exception e) {
System.err.println("에러입니당");
throw new RuntimeException(e);
}
}
}
- java.lang.System 클래스에 정의되어 있으며 자바에서 제공하는 표준 입려과 표준 출력을 다루는 스트림임
System.in
- 표준 입력 스트림(Standard Input Stream)으로서 사용자로부터 데이터를 입력받을 때 사용됨
- 기본적으로 키보드로부터 데이터를 읽음
- java.io.InputStrea 클래스의 객체로 사용됨
System.out
- 표준 출력 스트림(Standard Output Stream)으로서 데이터를 콘솔에 출력할 때 사용됨
- 기본적으로 콘솔(터미널)로 데이터를 출력함
- java.io.PrintStream 클래스의 객체로 사용됨
System.err
- 표준 오류 스트림(Standard Error Output Stream)으로서 오류 및 예외와 같은 에러 메시지를 콘솔에 출력할 때 사용됨
- 기본적으로 콘솔(터미널)로 데이터를 출력함
- java.io.InputStrea 클래스의 객체로 사용됨
파일 읽고 쓰기
public class FileInputOutputStream {
public static void main(String[] args) {
try (FileReader fr = new FileReader("hello.txt");
FileWriter fw = new FileWriter("hello2.txt")) {
char[] buffer = new char[1024];
int charsRead;
while ((charsRead = fr.read(buffer)) != -1) {
fw.write(buffer, 0, charsRead);
}
System.out.println("파일이 복사되었습니다.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
백기선님 팁
- BufferedInputStream이 성능상의 이점을 가지는 이유
- Buffer는 데이터를 전송하는 상호간의 장치에서 고속의 장치(Java API 호출)와 저속의 장치(OS 시스템 콜) 간의 속도 차이로 인해 저속의 장치가 작업을 추리하는 동안 고속의 장치가 기다려야하는 현상을 줄여줌
- 한 바이트씩 인풋 아웃풋을 보내는게 아니라 모아서 보내니까 시스템 콜(입출력) 횟수가 줄어서 성능상의 이점이 생김
- 한 바이트씩 쓰는거는 물을 한모금 마시고 또 한모금 마시러 냉장고에 갔다오고 또 한모금 마시러 냉장고에 갔다오는 형태임 -> 물을 한컵씩 떠오는 것이 버퍼임, 컵이 버퍼의 형태임
- 뭔가하다가 막혔다라는 생각이들면 바로 질문을 해야됨, 더 이상 어떻게 해야될지 모르겠을 때, 도움이 필요할 때
- IO는 로우레벨임
- NIO는 버퍼기반 채널인터페이스를 이용해서 사용하면 양방향 입출력이 가능함
- 블록킹(blocking)은 입력 스트림의 read() 메소드를 호출하면 데이터가 입력되기 전까지 Thread는 블록킹(대기상태)가 됨
- 넌블록킹(non-blocking)은 Thread를 interrupt하여 빠져나오고 다른작업을 할 수있게됨, 훨씬 더 효율적으로 리소스를 사용할 수 있음
- 다이렉트 버퍼, 논다이렉트 버퍼 개념 알아두기
- 다이렉트 버퍼 = buffer를 사용해서 jvm 밖에는 메모리를 사용하는 버퍼를 만드는 것, jvm에 할당된 메모리보다 더큰 메모리를 사용하는 애플리케이션을 만들 수 있음, 훨씬 더 빠름, 이니셜라이즈할때 jvm에서 버퍼를만드는 거 보다 버퍼를 만드는게 조금 느림
- in memory data grid(IMDG)에 대해서도 한번 찾아보기
- bytebuffer로 db만들기 https://blogs.oracle.com/javamagazine/post/creating-a-java-off-heap-in-memory-database
- 데코레이터 패턴의 전형적인 예제 java.io 패키지
- 직렬화에 대해 알아보자, serialVersionUID
- 직렬화 = 이삿짐을 싼다고 생각하자, 역직렬화 = 짐을 풀어 해치는과정
- 세션을 서로간에 동기화하는 것(세션마다 동기화하는 방법은 메모리가 많이 필요함)보다는 세션클러스터링 서버를 만드는게 좀더 효율적이고 편리함
- sticky session을 사용해도됨
- 결국 stream은 최하단에서 사용되며 byte단위로 읽으면 IO이고 buffer단위로 읽으면 nio라고 생각하면됨, 채널을 사용한다는 거는 양방향, 멀티플렉싱이 가능한 개념적인거고 밑단의 기본은 stream임