실제의 입력이나 출력이 표현된 데이터의 이상화된 흐름을 의미
즉, 스트림은 운영체제에 의해 생성되는 가상의 연결 고리를 의미하며, 중간 매개자 역할을 한다
자바에서는 파일이나 콘솔의 입출력을 직접 다루지 않고, 스트림을 통해 다룹니다.
+) Java 8 부터 추가된 Stream API와는 전혀 다른 개념임
입출력 스트림
스트림은 한 방향으로만 통신할 수 있으므로, 입력과 출력을 동시에 처리할 수는 없기 때문에 사용 목적에 따라 입력 스트림과 출력 스트림으로 구분한다
자바에서는 java.io 패키지를 통해 InputStream과 OutputStream 클래스를 별도로 제공하고 있다
자바에서의 스트림 생성은 이러한 스트림 클래스 타입의 인스턴스를 생성하는 것을 의미함
InputStream 클래스 메소드
abstract int read()
: 해당 입력 스트림으로부터 다음 바이트를 읽어들임.
int read(byte[] b)
: 해당 입력 스트림으로부터 특정 바이트를 읽어들인 후, 배열 b에 저장함.
int read(byte[] b, int off, int len)
: 해당 입력 스트림으로부터 len 바이트를 읽어들인 후, 배열 b[off]부터 저장함.
여기서 byte로 읽는데 왜 int형이 반환되지?
더이상 읽어들일 바이트가 없으면 -1을 반환해야 하는데 byte 타입은 -1을 표현할 수 없기 때문에 int형을 반환값으로 선언한다
OutputStream 클래스 메소드
abstract void write(int b)
: 해당 출력 스트림에 특정 바이트를 저장함
void write(byte[] b)
: 배열 b의 특정 바이트를 배열 b의 길이만큼 해당 출력 스트림에 저장함
void write(byte[] b, int off, int len)
: 배열 b[off]부터 len 바이트를 해당 출력 스트림에 저장함
InputStream 클래스에는 read() 메소드가, OutputStream 클래스에는 write() 메소드가 각각 추상 메소드로 구현되어 있고 사용자는 이 메소드를 상황에 맞게 잘 구현해야한 입출력 스트림을 생성하여 사용할 수 있다
자바 입출력 스트림은 크게 바이트 스트림, 문자 스트림 으로 구분된다
자바 바이트 스트림
이미지, 오디오, 동영상과 같은 바이너리 데이터를 처리할 때 사용된다
자바에서 스트림은 기본적으로 바이트 단위로 데이터를 전송하고 이를 위해 다양한 바이트 기반의 입출력 스트림을 제공한다
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteStreamExample {
public static void main(String[] args) {
byte[] data = {1, 2, 3, 4, 5};
// 파일에 바이트 쓰기
try (FileOutputStream out = new FileOutputStream("byteData.bin")) {
out.write(data);
} catch (IOException e) {
e.printStackTrace();
}
// 파일에서 바이트 읽기
try (FileInputStream in = new FileInputStream("byteData.bin")) {
int byteRead;
while ((byteRead = in.read()) != -1) {
System.out.print(byteRead + " ");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
문자 기반 스트림
자바에서 스트림은 기본적으로 바이트 단위로 데이터를 전송하지만 자바에서 가장 작은 타입인 char 형이 2바이트 이므로, 1바이트씩 전송되는 바이트 기반 스트림으로는 원활한 처리가 힘든 경우가 많아서 생김
문자 기반 스트림은 기존의 바이트 기반 스트림에서 InputStream을 Reader로, OutputStream을 Writer로 변경하면 사용할 수 있다
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CharStreamExample {
public static void main(String[] args) {
String data = "Hello, Java!";
// 파일에 문자 쓰기
try (FileWriter writer = new FileWriter("textData.txt")) {
writer.write(data);
} catch (IOException e) {
e.printStackTrace();
}
// 파일에서 문자 읽기
try (FileReader reader = new FileReader("textData.txt")) {
int charRead;
while ((charRead = reader.read()) != -1) {
System.out.print((char) charRead);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
콘솔과 같은 표준 입출력 장치를 위해 java.lan 패키지에서 System이라는 표준 입출력 클래스를 정의하고 있다
표준 입출력을 위해 제공되는 클래스 변수
System.in
: InputStream 콘솔로부터 데이터를 입력받음.System.out
: PrintStream 콘솔로 데이터를 출력함.System.err
: PrintStream 콘솔로 데이터를 출력함.표준 입출력 스트림은 자바가 자동으로 생성하므로, 개발자가 별도로 스트림을 생성하지 않아도 사용할 수 있다
위의 3가지 입출력 스트림은 기본적으로 콘솔과 같은 표준 입출력 장치를 대상으로 한다
만약 다른 입출력 장치로 스트림의 대상을 바꾸고 싶으면 아래와 같이 하면 된다
RandomAccessFile 클래스
입출력 스트림을 이용하면 파일에 순차적으로 입출력 작업을 수행할 수 있지만 순차적인 접근이 아닌 임의의 지점에 접근하여 작업을 수행하고 싶을 때 사용
이 클래스는 파일만을 대상으로 하며, 임의의 지점에서 입출력을 동시에 수행할 수 있다
import java.io.*;
public class RandomAccessFileExample {
public static void main(String[] args) {
try {
// RandomAccessFile을 읽기 모드로 열기
RandomAccessFile raf = new RandomAccessFile("example.txt", "r");
// 파일 포인터를 원하는 위치(예: 50번째 바이트)로 이동
raf.seek(50);
// 데이터 읽기
byte[] data = new byte[100]; // 최대 100바이트 읽기
raf.read(data);
// 읽은 데이터 출력
System.out.println(new String(data).trim()); // 문자열로 변환하여 출력
// 파일 닫기
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
예시와 같이 파일모드 (r: 읽기만, rw: 읽기/쓰기/없으면 생성)을 같이 입력해줘야 한다
getFilePointer() 메소드를 사용하면 파일 포인터의 현재 위치를 확인할 수 있다
또한, seek() 메소드를 사용하면 파일 포인터의 위치를 변경할 수도 있다
File 클래스
이전의 입출력 스트림을 사용하면 파일을 통한 입출력 작업을 수행할 수 있지만 파일의 제거나 디렉터리에 관한 작업 등은 입출력 스트림을 통해서는 수행할 수 없다
따라서 이러한 입출력 작업 이외의 파일과 디렉터리에 관한 작업을 File 클래스를 통해 처리하도록 하고 있다
public static void main(String[] args){
File dir = new File("D:\\data"); // 디렉터리 생성
File file = new File(dir, "data.txt"); // 파일 생성
if(!file.exists()) { // 파일이 생성되었는지를 확인함.
System.out.println("파일이 존재하지 않습니다.");
System.exit(0);
}
System.out.println(file.getPath()); // D:\data\data.txt
System.out.println(file.length()); // 파일의 크기를 반환함.
}