I/O, 표준 입출력
자바 I/O
Input(입력)과 Output(출력) 약자이다. 데이터를 읽고 쓰는 기능을 말하며, 파일, 네트워크, 다른 프로그램 등 다양한 소스와 대상으로부터 데이터를 주고받는데 사용된다.
Stream 스트림
스트림은 데이터를 순차적으로 처리하는 일련의 연속된 데이터 요소들입니다. 자바에서 스트림은 데이터의 흐름을 읽고 쓰는데 사용되는 추상적인 개념이다. 입출력 작업을 수행할 때 데이터를 한 곳에서 다른 곳으로 이동시키는 매개체 역할을 한다.
스트림은 두 가지 맥락에서 사용된다. 하나는 I/O에 관련된 데이터 흐름을 처리하는 것이다. 다른 하나는 자바 8 이후에 도입된 java.util.stream 패키지에 포함된 스트림 API이다.
java.io 패키지
자바 I/O 스트림 API에는 실제로 여러 인터페이스와 추상 클래스가 정의되어 있다.
바이트 기반 스트림
InputStream / OutputStream
해당 클래스들은 데이터를 바이트 단위로 읽고 쓰는 기본적인 기능을 제공한다.
ex ) FileInputStream, FileOutputStream 파일로부터 바이트를 읽거나 파일에 바이트를 쓰는데 사용
문자 기반 스트림
Reader / Writer
바이트 기반 스트림과 달리 문자 기반 스트림은 문자 데이터의 입출력을 처리한다. 인코딩 문제를 자동으로 처리해주기 때문에 텍스트 데이터를 다룰 때 특히 유용하다.
ex ) FileReader, FileWriter 텍스트 파일을 읽고 쓸 때 사용된다.
버퍼링 스트림
BufferedInputStream / BufferedOutputStream / BufferedReader / BufferedWriter
입출력 효율을 증가시키기 위해 내부적으로 버퍼를 사용한다. 버퍼링 스트림은 데이터를 일정량 모아 놓았다가 한 번에 처리하기 때문에 크기가 적은 입출력 연산이 많이 필요할 때 성능을 향상시킬 수 있다.
고수준의 스트림
DataInputStream / DataOutputStream
자바의 기본 데이터 타입을 쉽게 읽고 쓸 수 있게 해줍니다. 예를 들어 정수, 실수 등을 데이터 소스로부터 직접 읽거나 쓸 수 있다.
ObjectInputStream / ObjectOutputStream
객체 직렬화에 사용되며, 객체를 바이트 형태로 변환하거나 바이트에서 객체를 복원하는 기능을 제공한다.
스트림 두 가지 주요 특성
■ 단방향성
스트림은 단방향으로만 데이터를 처리한다. 즉 입력 스트림은 데이터를 읽는데 사용되며, 출력 스트림은 데이터를 쓰는 데 사용된다. 두 작업을 동시에 수행하려면 입력 스트림과 출력 스트림을 별도로 사용해야 한다.
■ 순차적 접근
스트림은 데이터에 순차적으로 접근한다. 이는 스트림을 통해 데이터를 처리할 때 한 번에 하나의 데이터 요소만 처리하며 일반적으로 한 번 처리된 데이터 요소는 다시 되돌아갈 수 없다는 의미이다. (일부 스트림은 마킹 기능을 제공하여 이러한 제한을 일부 완화한다.)
즉 스트림은 먼저 보낸 데이터를 먼저 받는다. 또한 중간에 건너뜀 없이 연속적으로 데이터를 주고 받는다. 이 특성은 큐(Queue)의 FIFO(First In First Out)구조와 유사하다.
표준 입출력
자바에서 표준 입출력은 프로그램과 사용자 간의 기본적인 데이터 교환 방법을 제공한다.
표준 입출력 핵심 요소
■ System.in
System.in은 자바 프로그램의 표준 입력 스트림이다. 기본적으로 이 스트림은 키보드 입력을 받는다. 이는 InputStream 객체로 바이트 기반에 수준 낮은 입력 기능을 제공한다. 일반적으로 사용자로부터 텍스트를 읽기 위해 Scanner 클래스나 BufferedReader과 같은 더 수준 높은 래퍼(wrapper)를 사용하여 쉽게 데이터를 처리할 수 있다.
■ System.out
System.out는 자바 프로그램의 표준 출력 스트림이다. 텍스트 데이터를 콘솔에 출력한다. 이 스트림은 PrintStream 객체로 println(), print(), printf() 등 여러 메소드를 제공하여 다양한 방식으로 데이터를 콘솔에 출력할 수 있다.
■ System.err
System.err도 PrintStream객체이다. 주로 에러 메시지나 중요한 경고 메시지를 콘솔에 출력할 때 사용된다. System.out과 기능적으로 동일하지만 일반 출력과 에러 출력을 구분하여 사용자에게 보여주는 데 도움을 준다.
import java.io.IOException;
public class MySystemIn {
public static void main(String[] args) {
// 키보드에서 데이터를 받아 응용 프로그램 안으로 넣기
System.out.println("알파벳 하나를 쓰고 Enter를 누르세요");
// 바이트 기반으로 동작 --> 바이트는 0과 1로 구성
// 정수요소 - byte, short, int, long
int i;
// 한 바이트씩 키보드에 값을 읽어라
try {
// 97 != 13 ---> true (13은 enter키이다.)
while ((i = System.in.read()) != '\n') {
System.out.println("i : " + i);
System.out.println("(char)i : " + (char) i);
}
i = System.in.read();
System.out.println("---------------");
// 출력
System.out.println("i : " + i);
// 문자로 변환하고 싶다면 --> 컴퓨터 안에 있는 문자표를 활용
System.out.println((char) i);
} catch (IOException e) {
e.printStackTrace();
}
} // end of main
} // end of class
import java.io.IOException;
public class MySystemIn2 {
public static void main(String[] args) {
// 키보드에서 데이터를 응용 프로그램 안으로 넣기
System.out.println("알파벳 하나를 쓰고 Enter를 누르세요");
// 바이트 동작 --> 0과1로 구성
// 정수 -> byte, short, int, long
int i;
// 한 바이트씩 키보드에 값을 읽어라
try {
// 97 != 13 --> true
while( ( i = System.in.read() ) != '\n' ) {
System.out.println(" i : " + i);
System.out.println(" (char)i : " + (char)i);
}
} catch (IOException e) {
e.printStackTrace();
}
} // end of main
}
기반 스트림(Base Stream)
기반 스트림은 데이터 소스와 직접 연결되는 스트림이다. 실제 데이터 읽기나 쓰기 작업을 수행한다. 이 스트림들은 데이터의 실제 입력 소스 (파일, 메모리, 네트워크 연결 등)에 직접 연결되어 데이터를 바이트 단위로 읽거나 쓰는 기본적인 기능을 제공한다.
■ 바이트 기반 스트림 : FileInputStream, FileOutputStream, SocketInputStream, SocketOutputStream 등이 있다. 이들은 바이트 데이터를 처리한다.
■ 문자 기반 스트림 : FileReader, FileWriter 등이 있다. 문자 데이터를 처리한다.
보조 스트림(Filter Stream)
보조 스트림은 기반 스트림 또는 다른 보조 스트림에 연결되어 사용된다. 이 스트림들은 데이터를 변환하거나 추가 기능을 제공하거나 성능을 향상시키는 역할을 한다. 보조 스트림은 자체적으로 데이터 소스에 연결되지 않으며 항상 다른 스트림을 필요로 한다.
■ 버퍼링 : BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter은 내부적으로 버퍼를 사용하여 데이터를 모아서 한 번에 처리한다. 이는 입출력 효율을 대폭 향상시킨다.
■ 데이터 변환 : DataInputStream, DataOutputStream은 기본 데이터 타입(int, float, long 등)을 쉽게 읽고 쓸 수 있게 해준다.
■ 객체 직렬화 : ObjectInputStream, ObjectOutputStream은 객체를 바이트 스트림으로 직렬화하거나 바이트 스트림에서 객체를 복원하는 기능을 제공한다.
■ 문자 인코딩 : InputStreamReader, OutputStreamWriter은 바이트 스트림을 문자 스트림으로 변환하거나 반대 작업을 할 때 사용되며, 인코딩 변환을 처리한다.
FileInputStream fis = new FileInputStream("input.txt"); // 기반 스트림
BufferedInputStream bis = new BufferedInputStream(fis); // 보조 스트림