보조 스트림이란 다른 스트림과 연결이 되어 여러 가지 편리한 기능을 제공해주는 스트림을 말한다. 보조스트림은 자체적으로 입출력을 수행할 수 없기 때문에 입출력 소스와 바로 연결되는 InputStream, OutputStream, Reader, Writer 등에 연결해서 입출력을 수행한다. 보조 스트림은 문자 변환, 입출력 성능 향상, 기본 타입 입출력 등의 기능을 제공한다.
프로그램은 입력 스트림으로부터 직접 데이터를 읽지 않고, 보조 스트림에서 제공하는 기능을 이용해서 데이터를 읽습니다. 반대로 출력 스트림으로 직접 데이터를 보내지 않고 보조 스트림에서 제공하는 기능을 이용해서 데이터를 보낸다.
보조 스트림 연결하기
보조 스트림을 연결하려면 보조 스트림을 생성할 때 자신이 연결될 스트림을 아래와 같이 생성자의 매개값으로 제공하면 된다.
보조스트림 변수 = new 보조스트림(연결스트림);
예를 들어 InputStream을 문자 변환 보조 스트림인 InputStreamReader에 연결하는 코드는 아래와 같다.
InputStream is = ...;
InputStreamReader reader = new InputStreamReader(is);
보조 스트림의 생성자 매개값은 InputStream, OutputStream, Reader, Writer 이외에 또 다른 보조 스트림이 될 수 있다. 이 말은 보조 스트림을 연속적으로 연결할 수 있다는 뜻이다.
예를 들어 아래와 같이 문자 변환 보조 스트림인 InputStreamReader를다시 성능 향상 보조 스트림인 BufferedReader에 연결할 수 있다.
InputStream = System.in;
InputStreamReader reader = new InputStreamReader(is);
BufferedReader br = new BufferedReader(reader);
문자 변환 보조 스트림
소스 스트림이 바이트 기반 스트림(InputStream, OutputStream, FileInputStream, FileOutputStream)이면서 입출력 데이터가 문자라면 Reader와 Writer로 변환해서 사용하는 것을 고려할 수 있다. 그 이유는 문자 입출력은 Reader와 Writer가 편리하기 때문이다.
OutputStreamWriter
OutputStreamWriter는 바이트 기반 출력 스트림에 연결되어 문자 출력 스트림인 Writer로 변환하는 보조 스트림이다.
Writer writer = new OutputStreamWriter(바이트 기반 출력 스트림);
예를 들어 파일 출력을 위한 바이트 기반 FileOutputStream을 아래와 같이 Writer 타입으로 변환할 수 있다.
FileOutputStream fos = new FileOutputStream("C:/Temp/test.txt");
Writer writer = new OutputSttreamWriter(fos);
InputStreamReader
InputStreamReader는 바이트 기반 입력 스트림에 연결되어 문자 입력 스트림인 Reader로 변환하는 보조 스트림이다.
Reader reader = new InputStreamReader(바이트 기반 입력 스트림);
예를 들어 파일 입력을 위한 바이트 기반 FileInputStream을 아래와 같이 Reader 타입으로 변환할 수 있다.
FileInputStream fis = new FileInputStream("C:/Temp/test.txt");
Reader reader = new InputStreamReader(fis);
성능 향상 보조 스트림
프로그램의 실행 성능은 입출력이 가장 늦은 장치를 따라간다. CPU와 메모리가 아무리 뛰어나도 하드 디스크의 입출력이 늦어지면 프로그램의 실행 성능은 하드 디스크의 처리 속도에 맞춰진다. 네트워크로 데이터를 전송할 때도 마찬가지이다. 느린 네트워크 환경이라면 컴퓨터 사양이 아무리 좋아도 메신저와 게임의 속도는 느릴 수밖에 없다.
이 문제에 대한 완전한 해결책은 될 수 없지만, 프로그램이 입출력 소스와 직접 작업하지 않고 중간에 메모리 버퍼와 작업함으로써 실행 성능을 향상시킬 수 있다. 예를 들어 프로그램은 직접 하드 디스크에 데이털르 보내지 않고 메모리 버퍼에 데이털르 보냄으로써 쓰기 속도가 향상된다. 버퍼는 데이터가 쌓이기를 기다렸다가 꽉 차게 되면 데이털르 한꺼번에 하드 디스크로 보냄으로써 출력 횟수를 줄여준다.
기본적으로 출력 스트림은 내부에 작은 버퍼를 가지고 있다. 하지만 이것만으로는 불충분하다. 보조 스트림 중에서는 위와 같이 메모리 버퍼를 추가로 제공하여 프로그램의 실행 성능을 향상시키는 것들이 있다. 바이트 기반 스트림에서는 BufferedInputStream, BufferedOutputStream이 있고 문자 기반 스트림에는 BufferedReader, BufferedWriter가 있다.
BufferedOutputStream과 BufferedWriter
BufferedOutputStream은 바이트 기반 출력 스트림에 연결되어 버퍼를 제공해주는 보조 스트림이고, BufferedWriter는 문자 기반 출력 스트림에 연결되어 버퍼를 제공해주는 보조 스트림이다.
BufferedOutputStream과 BufferedWriter는 프로그램에서 전송한 데이털르 내부 버퍼에 쌓아 두었다가 버퍼가 꽉 차면, 버퍼의 모든 데이터를 한꺼번에 보낸다. 프로그램 입장에서 보면 메모리 버퍼로 데이터를 고속 전송하기 때문에 출력 성능이 향상되는 효과를 얻게 된다.
BufferedOutputStream과 BufferedWriter 보조 스트림은 아래와 같이 생성자의 매개값으로 준 출력 스트림과 연결되어 추가적인 내부 버퍼를 제공한다.
BufferedOutputStream bos = new BufferedOutputStream(바이트 기반 출력 스트림);
BufferedWriter bw = new BufferedWriter(문자 기반 출력 스트림);
BufferedInputStream과 BufferedReader
BufferedInputStream은 바이트 기반 입력 스트림에 연결되어 버퍼를 제공해주는 보조 스트림이고, BufferedReader는 문자 기반 입력 스트림에 연결되어 버퍼를 제공해주는 보조 스트림이다. BufferedInputStream과 BufferedReader는 입력 소스로부터 자신의 내부 버퍼 크기만큼 데이터를 미리 읽고 버퍼에 저장해둔다. 프로그램은 외부의 입력 소스로부터 직접 읽는 대신 버퍼로부터 읽음으로써 읽기 성능이 향상된다.
BufferedInputStream과 BufferedReader 보조 스트림은 아래와 같이 생성자의 매개값으로 준 입력 스트림과 연결되어 추가적인 내부 버퍼를 제공한다.
BufferedInputStream bis = new BufferedInputStream(바이트 기반 입력 스트림);
BufferedReader br = new BufferedReader(문자 기반 입력 스트림);
기본 타입 입출력 보조 스트림
DataInputStream과 DataOutputStream 보조 스트림을 연결하면 기본 타입을 입출력할 수 있다.
아래는 DataInputStream과 DataOutputStream 객체를 생성하는 코드이다. 다른 보조 스트림과 마찬가지로 연결할 바이트 입출력 스트림을 생성자의 매개값으로 주면 된다.
DataInputStream dis = new DataInputStream(바이트 기반 입력 스트림);
DataOutputStream dos = new DataOutputStream(바이트 기반 출력 스트림);
아래는 기본 타입을 입출력하기 위해 DataInputStream과 DataOutputStream이 제공하는 메소드들이다.
| DataInputStream | DataOutputStream | ||
| boolean | readBoolean() | void | writeBoolean(boolean v) |
| byte | readByte() | void | writeByte(int v) |
| char | readChar() | void | writeChar(int v) |
| double | readDouble() | void | writeDouble(double v) |
| float | readFloat() | void | writeFloat(float v) |
| int | readInt() | void | writeInt(int v) |
| long | readLong() | void | writeLong(long v) |
| shrot | readShort() | void | writeShort(int v) |
| String | readUTF() | void | writeUTF(String str) |
이 메소드로 입출력할 때 주의할 점이 있는 데, 데이터 타입의 크기가 모두 다르므로 DataOUtputStream 으로 출력한 데이터를 다시 DataInputStream으로 읽어올 때는 출력한 순서와 동일한 순서로 읽어야 한다. 예를 들어 출력할 때의 순서가 int -> boolean -> double 이라면 읽을 때의 순서도 int -> boolean -> double 순이어야 한다.
프린터 보조 스트림
PrintStream과 PrintWriter는 프린터와 유사하게 출력하는 print(), println()) 메소드를 가지고 있는 보조 스트림이다. 매우 빈번히 사용한 콘솔 출력 스트림인 System.out이 바로 PrintStream 타입이기 때문에 print(), println() 메소드를 사용할 수 있었다. PrintStream은 바이트 기반 출력 스트림과 연결되고, PrintWriter는 문자 기반 출력 스트림과 연결된다.
PrintStream과 PrintWriter는 다른 보조 스트림과 마찬가지로 연결할 출력 스트림을 생성자의 매개값으로 받는다.
PrintStream ps = new PrintStream(바이트 기반 출력 스트림);
PrintWriter pw = new PrintWriter(문자 기반 출력 스트림);
println(0 메소드는 출력할 데이터 끝에 개행 문자인 '\n'을 추가한다. 그래서 콘솔이나 파일에서 줄 바꿈이 일어난다. 그러나 print() 메소드는 '\n'을 추가하지 않기 때문에 개행 없이 문자를 출력한다. println()과 print() 메소드는 출력할 데이터 타입에 따라 아래와 같이 오버로딩이 되어 있다.
| PrintStream / PrintWriter | |||
| void | print(boolean b) | void | println(booelan b) |
| void | print(char c) | void | println(char c) |
| void | print(double d) | void | println(double d) |
| void | print(float f) | void | println(float f) |
| void | print(int i) | void | println(int i) |
| void | print(long l) | void | println(long l) |
| void | print(Object obj) | void | println(Object obj) |
| void | print(String s) | void | println(String s) |
| void | println() | ||
객체 입출력 보조 스트림
ObjectOutputStream과 ObjectInputStream 보조 스트림을 이용하면 메모리에 생성된 객체를 파일 또는 네트워크로 출력할 수 있다.
ObjectOutputStream은 객첼르 직렬화하는 역할을 하고, ObjectInputStream은 객체를 역직렬화하는 역할을 한다. 직렬화란 객체를 바이트 배열로 만드는 것을 말하고, 역직렬화란 바이트 배열을 다시 객체로 복원하는 것을 말한다.
ObjectInputStream과 ObjectOutputStream은 다른 보조 스트림과 마찬가지로 연결할 바이트 기반 입출력 스트림을 생성자의 매개값으로 받는다.
ObjectInputStream ois = new ObjectInputStream(바이트 기반 입력 스트림);
ObjectOutputStream oos = new ObjectOutputStream(바이트 기반 출력 스트림);
ObjectOutputStream의 wirteObject() 메소드는 객체를 직렬화해서 출력 스트림으로 보낸다.
oos.writeObject(객체);
반대로 ObjectInputStream의 readObject() 메소드는 입력 스트림에서 읽은 바이트를 역직렬화해서 객체로 다시 복원해서 리턴한다. 리턴 타입은 Object 타입이기 때문에 원래 타입으로 아래와 같이 강제 변환해야 한다.
객체타입 변수 = (개체타입)ois.readObject();
자바는 모든 객체를 직렬화하지 않는다. java.io.Serializable 인터페이스를 구현한 객체만 직렬화한다. Serializable 인터페이스는 메소드 선언이 없는 인터페이스이다. 객체를 파일로 저장하거나, 네트워크로 전송할 목적이라면 개발자는 클래스를 선언할 때 implements Serializable을 추가해야 한다. 이것은 개발자가 JVM에게 직렬화해도 좋다고 승인하는 역할을 한다고 보면 된다.
public class XXX implements Serializable{
...
}'Java' 카테고리의 다른 글
| 자바 입출력 관련 API (1) | 2023.06.30 |
|---|---|
| 자바 입출력 스트림 (0) | 2023.06.29 |
| 자바 LIFO와 FIFO 컬렉션 (0) | 2023.06.29 |
| 자바 컬렉션 프레임워크 (0) | 2023.06.29 |
| 자바 스레드 제어 (0) | 2023.06.29 |