본문 바로가기

JAVA/혼공자

[혼공자] Chapter 14-2 보조 스트림

❓보조 스트림이란❓
보조 스트림이 필요한 이유 ))
기본 스트림에서 제공하는 기능에 더하여 데이터를 변환하거나, 형식을 지정하거나, 성능을 향상시키기 위해서 사용한다.
보조 스트림의 특징 ))
자체적으로 입출력을 할 수 없어서 다른 스트림과 연결해서 사용해야 한다.
이는 생성자의 매개값에 연결하려는 스트림을 전달함으로써 이루어진다.
→ 뒤에 나오는 보조 스트림 모두 보조스트림 변수명 = new 보조스트림(연결할 스트림 객체); 형식으로 선언된다.
이때 연결할 스트림은 기본 스트림(InputStream, OutputStream / Reader, Writer) 뿐 아니라 다른 보조 스트림과도 가능하다.

▶ 문자 변환 보조 스트림
복습 ) 바이트 기반 스트림은 모든 데이터를 다룰 수 있지만, 문자 기반 스트림은 문자 데이터에 특화되어있다.
따라서 사용하는 스트림이 바이트 기반인데 입출력 데이터가 문자라면,
스트림을 '문자 기반'으로 바꾸는 것이 더 적절할 것이다.
문자 변환 보조 스트림은 이렇듯 바이트 기반 스트림을 문자 기반 스트림으로 변환해주는 역할을 한다.

▷ OutputStreamWriter / InputStreamReader
바이트 기반 스트림에 연결되어 문자 기반 스트림으로 변환하는 보조 스트림이다.

사용 방법
OutputStreamWriter/InputStreamReader의 생성자에 연결할 바이트 기반 스트림을 주고, Writer/Reader 타입으로 받으면 된다. 
그렇게 하면 자동 형변환하듯 문자 기반 스트림으로 사용할 수 있다.

// 가장 대표적인 예시 - 이후 BufferedReader에 연결된다.
InputStream is = System.in;
InputStreamReader = new InputStreamReader(is);

// 파일과 연결된 문자 변환 보조 스트림
FileOutputStream fos = new FileOutputStream("C:/Tempt/test.txt");
Writer writer = new OutputStreamWriter(fos);

 


▶ 성능 향상 보조 스트림
프로그램의 실행 성능은 입출력이 가장 늦은 장치를 따라간다.
따라서 프로그램의 입출력 속도를 향상 시키는 것이 곧 프로그램의 성능을 향상시키는 것이다.
이를 위해 버퍼를 사용할 수 있다.

출력 : 하드디스크에 직접 데이터를 보내지 않고 메모리 버퍼에 쌓아뒀다가 한꺼번에 버퍼에 있는 내용을 출력한다.
장점 )) 프로그램 → 하드디스크로 [5회 출력]할 것을
프로그램 → 버퍼 5회 고속 전송 → 하드 디스크로 [1회 출력]하여 출력 횟수를 줄일 수 있다.
또한, 프로그램 입장에서 데이터를 빨리빨리 내보낼 수 있다.

입력 : 입력 소스로부터 버퍼에 데이터를 미리 채워두고 프로그램은 버퍼에 있는 데이터를 읽는다.
장점 : 프로그램이 소스로부터 하나하나 읽어오는 것이 아니라 
미리 준비된 데이터를 고속으로 읽기만 하면 되므로 읽기 성능이 향상된다.

기본적으로 스트림은 내부에 버퍼를 가지고 있지만, 
보조 스트림을 사용해 추가 버퍼를 이용하여 프로그램의 실행 성능을 향상시킬 수 있다.
추가 버퍼를 제공하는 보조 스트림은 기본 스트림 클래스에 Buffered를 붙인 이름이다.
(BufferedOutputStream / BufferedWriter / BufferedInputStream / BufferedReader)

이 중 문자열 기반 입력 스트림과 연결하는 BufferedReader는 readLine( ) 메소드를 제공한다.
readLine( ) 메소드는 엔터 "\r\n" 이전까지의 모든 문자열을 읽고 리턴할 수 있다.
더이상 읽을 줄이 없으면 null을 리턴한다.
(그냥 read는 한 문자씩 리턴, 읽을 문자가 없으면 -1 리턴)

// Buffered~ 스트림은 다른 스트림과 연결하여 사용한다.
// 이때 '연결'하는 방법은 앞서 언급한 것처럼 생성자 매개변수로 다른 스트림 객체를 받는 걸 의미한다.
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));


▶ 기본 타입 입출력 보조 스트림
바이트 기반 입출력 스트림이 기본타입을 입출력할 수 있게 하는 스트림이다.
(기본 타입 : boolean, char, short, int, long, float, double)
DataInputStream, DataOutputStream을 바이트 기반 입출력 스트림에 연결해서 사용한다.
(기본 입출력 스트림으로는 byte, char 입출력만 가능했다.)
이때, 주의할 점이 있는데 기본형 데이터 타입은 크기가 다르므로 ex. int는 4byte, char은 2byte 등
DataOutputStream으로 출력한 순서대로 DataInputStream으로 읽어와야 한다는 것이다.

▶ 프린터 보조 스트림
PrintStream과 PrintWriter은 프린터와 유사하게 출력하는 print( ) 메소드를 제공하는 보조 스트림이다.
우리가 지금껏 사용했던 System.out 도 PrintStream의 한 종류이므로 print( )를 통해 콘솔에 출력할 수 있었다.

System.out으로 출력하는 과정 정리 : 
스트림은 매개체로서 자바 프로그램과 콘솔창을 연결하고 있다. 
스트림은 단방향이므로 프로그램에서 출력하기 위해 출력 스트림을 이용한다.
그리고 데이터를 모니터쪽으로 보내는 것에 그치지 않고 '프린터처럼 출력'하게 하기 위해 
프린터 보조 스트림인 System.out를 사용한다.
또 출력하게 하는 동작을 위해 println( ) 메소드를 사용한다.
목적지를 콘솔창이 아니라 메모장으로 한다면, 메모장에 그대로 출력된다.

▶ 객체 입출력 보조 스트림
ObjectOutputStream과 ObjectInputStream을 이용하면 객체를 입출력할 수 있다. 
둘 다 바이트 기반 입출력 스트림에 연결해서 사용한다.

ObjectOutputStream은 객체를 직렬화 하는 역할을 하고 ObjectInputStream은 객체를 역직렬화하는 역할을 한다.
직렬화란 객체를 바이트 배열로 만드는 것을 말하고, 역직렬화란 바이트 배열을 다시 객체로 복원하는 것을 말한다.
=> 객체를 바이트 배열로 만들면 0과 1로 이뤄진 배열이 만들어 질 것이므로 '직렬화'라고 외우면 될 것 같다.

프로그램에서는 객체를 다루고, 전송할 때는 바이트로 전송하므로 
[출력 = 객체→바이트 = 직렬화] [입력= 바이트→객체 = 역직렬화]로 외우면 쉽다.

직렬화할 때는 ObjectOutputStream의 객체에 writeObject 메소드를 호출해 매개값으로 직렬화하려는 객체를 전달하면 된다.

반대로 역직렬화할 때는 ObjectIntputStream의 객체에 readObject 메소드를 호출해 객체 타입으로 강제 형변환하면 된다.

[직렬화]
ObjectOutputStream oos = new ObjectOutputStream(바이트 기반 출력 스트림);
oos.writeObject(객체);

[역직렬화]
ObjectInputStream ois = new ObjectInputStream(바이트 기반 입력 스트림);
객체타입 변수 = (객체타입) ois.readObject( );

 

자바는 모든 객체를 직렬화하지 않는다.
java.io.Serialixable 인터페이스를 구현한 객체만 직렬화한다.
Serialixable 인터페이스는 메소드 선언이 없는 인터페이스이므로, 
구현해야 하는 추상메소드 없이 단지 '직렬화 해도 되는가'를 표시하기 위한 인터페이스이다.