✔ I/O 스트림에 대한 이해
스트림과 I/O 스트림의 차이는?
- 스트림은 데이터를 어떻게 원하는 형태로 걸러내고 가공할 것인가의 목적
- 반면 I/O 스트림은 어떻게 데이터를 입력하고 출력할 것인가의 목적
I/O 스트림 모델과 스트림의 이해
- 자바에서는 입출력 대상에 상관없이 동일한 방법으로 입출력을 진행할 수 있도록 I/O 스트림 모델을 정의
- 입출력 대상에는 파일, 키보드, 모니터, 그래픽카드, 사운드카드, 프린터, 팩스,
인터넷으로 연결되어 있는 서버 또는 클라이언트 등이 존재 - I/O 스트림 모델을 줄여서 I/O 모델이라고 함
- 가장 기본적인 데이터의 입출력 단위는 바이트이고,
바이트 단위로 데이터를 입력 및 출력하는 스트림을 바이트 스트림이라 함
- 입출력 대상에는 파일, 키보드, 모니터, 그래픽카드, 사운드카드, 프린터, 팩스,
- I/O 모델의 스트림은 입출력 대상과의 연결을 위한 클래스로 크게 입력 스트림과 출력 스트림으로 나뉨
- 입력 스트림은 실행 중인 자바 프로그램으로 데이터를 읽어 들이는 스트림, 즉 데이터의 입력 통로
- 출력 스트림은 실행 중인 자바 프로그램으로부터 데이터를 내보내는 스트림, 즉 데이터의 출력 통로
- 데이터의 읽기나 저장이 끝나면 close 메소드를 호출하여 생성했던 출력 스트림을 소멸해야 함
스트림이 소멸되면 열려 있는 파일은 닫히고 할당되었던 메모리 자원은 다시 사용할 수 있도록 반환됨
public abstract int read() throws IOExcpetion
public abstract void write(int b) throws IOExcpetion
public abstract int read(byte[] b) throws IOExcpetion
public abstract void write(byte[] b, int off, int len) throws IOExcpetion
public static void main(String[] args) throws IOException {
OutputStream out = new FileOutputStream("data.dat");
out.write(7);
out.close();
InputStream in = new FileInputStream("data.dat");
int data = in.read();
in.close();
}
- 이때 만약 입력 및 출력 스트림의 생성 과정에서 예외가 발생하여 스트림의 생성에 실패할 경우
스트림을 종료하는 close 메소드의 호출을 생략해야만 또다른 예외의 발생을 막을 수 있음
// try ~ catch ~ finally문으로 하나의 스트림 생성
public static void main(String[] args) throws IOException {
OutputStream out = null;
try {
out = new FileOutputStream("data.dat");
out.write(7);
}
finally {
if(out != null)
out.close();
}
}
// try-with-resources문으로 하나의 스트림 생성
public static void main(String[] args) {
try(OutputStream out = new FileOutputStream("data.dat")) {
out.write(7);
}
catch(IOException e) {
e.printStackTrace();
}
}
// try-with-resources문으로 두 개의 스트림 생성
Scanner sc = new Scanner(System.in);
String src = sc.nextLine();
String dst = sc.nextLine();
try(InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dst)) {
int data;
while(true) {
data = in.read(); // 파일로부터 읽어 들인 1바이트의 유효한 데이터에 3바이트의 0을 채워서 4바이트 int형 데이터로 반환
if(data == -1)
break;
out.write(data); // 인자로 전달되는 int형 데이터의 첫 번째 바이트만을 파일에 저장
}
}
catch(IOException e) {
e.printStacktrace();
}
// try-with-resources문으로 두 개의 스트림 생성
Scanner sc = new Scanner(System.in);
String src = sc.nextLine();
String dst = sc.nextLine();
try(InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dst)) {
// 많은 양의 데이터를 한 번에 읽고 쓰기 (1킬로바이트 단위로 데이터를 읽고 써 속도 향상)
byte buf[] = new byte[1024];
int len;
while(true) {
len = in.read(buf); // 배열 buf로 데이터를 읽어 들임
if(len == -1)
break;
out.write(buf, 0, len); // len 바이트만큼 데이터를 저장
}
}
catch(IOException e) {
e.printStacktrace();
}
✔ 필터 스트림의 이해와 활용
필터 스트림의 이해
- 필터 스트림은 입출력 스트림에 덧붙여서 데이터를 조합, 가공 및 분리하기 위한 클래스로
크게 필터 입력 스트림과 필터 출력 스트림으로 나뉨- 필터 입력 스트림은 입력 스트림에 연결되는 필터 스트림
- 필터 출력 스트림은 출력 스트림에 연결되는 필터 스트림
- 입력 스트림은 파일로부터 4개의 1바이트 데이터를 읽어서 필터 입력 스트림에 전달하게 되고
필터 입력 스트림은 이를 하나의 int형 데이터로 조합해서 반환함 - 필터 출력 스트림은 전달 받은 int형 데이터를 4개의 1바이트 데이터로 구분해서 출력 스트림에 전달
- 기본 자료형 데이터의 입력과 출력을 위한 필터 스트림으로는 DataInputStream, DataOutputStream이 존재
public final void writeInt(int v) throws IOException
public final void writeDouble(double v) throws IOException
public final int readInt() throws IOException
public final double readDouble() throws IOException
// try-with-resources문으로 각각 하나의 스트림 생성
public static void main(String[] args) {
try(DataOutputStream out = new DataOutputStream(new FileOutputStream("data.dat"))) {
out.writeInt(370);
out.writeDouble(3.14);
}
catch(IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try(DataInputStream in = new DataInputStream(new FileInputStream("data.dat"))) {
int num1 = in.readInt();
double num2 = int.readDouble();
}
catch(IOException e) {
e.printStackTrace();
}
}
버퍼링 기능을 제공하는 필터 스트림
- 버퍼링 기능을 제공하는 필터 스트림으로는 BufferedInputStream, BufferedOutputStream이 존재
- 버퍼 입력 스트림은 내부에 버퍼(메모리 공간)을 가지므로 많은 양의 데이터를 가져다가 버퍼를 채우고
프로그래머가 read 메소드를 호출할 때 버퍼 스트림의 버퍼에 저장된 데이터를 반환하게 되어 성능이 향상됨 - 쓸 때도 마찬가지로 버퍼 출력 스트림 내부에 데이터를 저장해 두었다가 한 번 파일에 쓰면 속도 저하를 줄일 수 있음
- 하지만 버퍼 스트림에 저장된 데이터가 파일에 저장되지 않은 상태에서 컴퓨터가 다운 되는 등의 상황이 발생할 수 있으므로
버퍼가 차지 않아도 파일에 저장해야 할 중요한 데이터가 있다면
flush 메소드를 통해 명시적으로 버퍼를 비워 파일로 데이터를 보내라고 명령할 수 있음
- 버퍼 입력 스트림은 내부에 버퍼(메모리 공간)을 가지므로 많은 양의 데이터를 가져다가 버퍼를 채우고
public void flush() thrwos IOException
// try-with-resources문으로 두 개의 스트림 생성
Scanner sc = new Scanner(System.in);
String src = sc.nextLine();
String dst = sc.nextLine();
try(BuffredInputStream in = BuffredInputStream(new FileInputStream(src));
BuffredOutputStream out = BuffredOutputStream(new FileOutputStream(dst))) {
int data;
while(true) {
datat = in.read();
if(data == -1)
break;
out.write(data);
}
}
catch(IOException e) {
e.printStacktrace();
}
- 파일을 대상으로 버퍼링 기능을 갖는 스트림을 생성하여 기본 자료형 데이터를 저장하기 위한 스트림도 생성 가능
- 이때 버퍼링 기능의 필터 스트림과 기본 자료형 데이터의 저장을 위한 필터 스트림이 동시에 필요
// try-with-resources문으로 각각 하나의 스트림 생성
public static void main(String[] args) {
try(DataOutputStream out = new DataOutputStream(new BuffredOutputStream((new FileOutputStream("data.dat")))) {
out.writeInt(370);
out.writeDouble(3.14);
}
catch(IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try(DataInputStream in = new DataInputStream(new BuffredInputStream(new FileInputStream("data.dat")))) {
int num1 = in.readInt();
double num2 = int.readDouble();
}
catch(IOException e) {
e.printStackTrace();
}
}
✔ 문자 스트림의 이해와 활용
문자 스트림의 이해
- 바이트 스트림은 입출력 과정에서 데이터의 변화 없이 바이트 단위로 데이터를 입력 및 출력함
- 반면 문자 스트림은 문자가 갖는 특성을 고려하여 입출력이 진행되어야 하므로 약간의 데이터 수정이 필요함
- 자바는 모든 문자를 유니코드를 기준으로 표현하지만, 운영체제의 문자 표현 방식이 자바의 문자 표현과 다름
- 그러므로 유니코드로 표현된 문자를 해당 운영체제의 문자 표현 방식으로 바꾸어서 저장해야 함
- 이를 위해 문자 입력 스트림과 문자 출력 스트림을 사용
InputStream // 바이트 입력 스트림의 상위 클래스
OutputStream // 바이트 출력 스트림의 상위 클래스
FileInputStream // 파일 대상 바이트 입력 스트림 생성
FileOutputStream // 파일 대상 바이트 출력 스트림 생성
↔
Reader // 문자 입력 스트림의 상위 클래스
Writer // 문자 출력 스트림의 상위 클래스
FileReader // 파일 대상 문자 입력 스트림 생성
FileWriter // 파일 대상 문자 출력 스트림 생성
public int read() throws IOException
try(Writer out = new FilterWrite("data.txt")) {
out.write('A');
out.write('한');
}
catch(IOException e) {
e.printStackTrace();
}
try(Reader in = new FilterReader("data.txt")) {
int ch;
while(true) {
ch = in.read();
if(ch == -1)
break;
System.out.println((char)ch);
}
}
catch(IOException e) {
e.printStackTrace();
}
버퍼링 기능을 제공하는 필터 스트림
- 문자 스트림에도 필터 스트림을 연결할 수 있음
BufferedInputStream // 바이트 기반 버퍼 입력 스트림
BufferedOutputStream // 바이트 기반 버퍼 출력 스트림
↔
BufferedReader // 문자 기반 버퍼 입력 스트림
BufferedWriter // 문자 기반 버퍼 출력 스트림
public String readLine() throws IOException
public void write(String s, int off, int len) throws IOException
String ks = "공부를 한다.");
try(BufferedWriter bw = new BufferedWriter(new FilterWrite("data.txt"))) {
bw.write(ks, 0, ks.length());
bw.newLine(); // 줄 바꿈
bw.write(ks, 0, ks.length());
}
catch(IOException) {
e.printStackTrace();
}
try(BufferedReader br = new BufferedReader(new FilterReader("data.txt"))) {
String str;
while(true) {
str = br.readLine(); // 한 문장 읽기
if(str == null)
break;
System.out.println(str);
}
}
catch(IOException) {
e.printStackTrace();
}
✔ I/O 스트림 기반의 인스턴스 저장
객체 직렬화와 객체 역 직렬화
- 바이트 스트림을 통해서 인스턴스를 통째로 저장하고 꺼내는 것도 가능함
- 인스턴스를 통째로 저장하는 것을 객체 직렬화, 인스턴스를 통째로 꺼내는 것을 객체 역 직렬화라고 함
- 이때 입출력 대상이 되는 인스턴스의 클래스는 직렬화 가능함을 표시하는 마커 인터페이스인 Serializable을 구현해야 하며
마커 인터페이스이므로 구현해야 할 메소드는 없음 - 인스턴스가 저장될 때 클래스가 참조하는 인스턴스도 Serializable을 구현하고 있을 경우 함께 저장이 이뤄짐
- 만약 함께 저장을 원하지 않을 경우 transient 선언을 추가하면 되며 복원했을 때 이 참조변수는 null 또는 0으로 초기화됨
ObjectInputStream // 인스턴스를 입력하는 스트림
ObjectOutputStream // 인스턴스를 출력하는 스트림
public final Object readObject() throws IOException, ClassNotFoundException
public fianl void writeObject(Object obj) throws IOException
public class SBox implements java.io.Serializable {
String s; // String 클래스는 Serializable을 구현하고 있으므로 참조변수가 참조하는 대상을 함께 저장됨
// transient String s; // 참조변수가 참조하는 대상을 저장하지 않음
public SBox(String s) {
this.s = s;
}
public String get() {
return s;
}
}
SBox box1 = new SBox("Robot");
SBox box2 = new SBox("Strawberry");
try(ObjectOutputStream oo = new ObjectOutputStream(new FilterWriter("Object.bin"))) {
oo.writeObject(box1); // 인스턴스 저장
oo.writeObject(box2);
}
catch(IOException) {
e.printStackTrace();
}
try(ObjectInputStream oi = new ObjectInputStream(new FilterReader("Object.bin"))) {
SBox box1 = (SBox) oi.readObject(); // 인스턴스 복원
System.out.println(box1.get());
SBox box2 = (SBox) oi.readObject();
System.out.println(box2.get());
}
}
catch(ClassNotFoundException e) {
e.printStackTrace();
}
catch(IOException e) {
e.printStackTrace();
}
'Java-Spring > 열혈 Java 프로그래밍' 카테고리의 다른 글
[Java] 쓰레드 그리고 동기화 (0) | 2023.08.19 |
---|---|
[Java] NIO 그리고 NIO.2 (0) | 2023.08.19 |
[Java] 시각과 날짜의 처리 (0) | 2023.08.18 |
[Java] 스트림 (0) | 2023.08.18 |
[Java] 메소드 참조와 Optional (0) | 2023.08.17 |