다른 스트림과 연결되어 여러 가지 편리한 기능을 제공해주는 스트림
FilterInputStream
, FilterOutputStream
의 하위 클래스이기 때문에 보조 스트림을 필터(filter) 스트림
이라고도 하지만 전부가 그런 것은 아니므로 사용 목적에 맞게 보조 스트림이라고 하는 것이 좋다.InputStream
, FileInputStream
, Reader
, FileReader
, 출력 소스와 바로 연결되는 OutputStream
, FileOutputStream
, Writer
, FileWriter
등에 연결해서 입출력을 수행한다.다음은 입력 스트림과 출력 스트림에 보조 스트림을 연결한 모습을 가상화시킨 것이다.
보조스트림 변수 = new 보조스트림(연결스트림)
InputStreamReader
에 연결하는 코드는 다음과 같다.InputStream is = System.in;
InputStreamReader reader = new InputStreamReader(is);
InputStreamReader
를 다시 성능 향상 보조 스트림인 BufferedReader
에 연결하는 코드는 다음과 같다.InputStream is = System.in;
InputStreamReader reader = new InputStreamReader(is);
BufferedReader br = new BufferedReader(reader);
소스 스트림이 바이트 기반 스트림(InputStream, OutputStream, FileInputStream, FileOutputStream)이면서 입출력 데이터가 문자라면
Reader
와Writer
로 변환해서 사용하는 것을 고려해야 한다.
Reader
와 Writer
는 문자 단위로 입출력하기 때문에
바이트 입력 스트림에 연결되어 문자 입력 스트림인
Reader
로 변환시키는 보조 스트림Reader reader = new InputStreamReader(바이트입력스트림);
InputStream
을 다음과 같이 Reader
타입으로 변환할 수 있다.InputStream is = System.in;
Reader reader = new InputStreamReader(is);
FileInputStream
도 다음과 같이 Reader
타입으로 변환시킬 수 있다.FileInputStream fis = new FileInputStream("C:/Temp/file.txt");
Reader reader = new InputStreamReader(fis);
FileInputStream
에 InputStreamReader
를 연결하지 않고 FileReader
를 직접 생성할 수도 있다.FileReader
는 InputStreamReader
의 하위 클래스이기 때문에 FileReader
가 내부적으로 FileInputStream
에 InputStreamReader
보조 스트림을 연결한 것이라고 볼 수 있다.바이트 출력 스트림에 연결되어 문자 출력 스트림인 Writer로 변환시키는 보조 스트림
Writer writer = new OutputStreamWriter(바이트출력스트림);
FileOutputStream
을 다음과 같이 Writer
타입으로 변환할 수 있다.FileOutputStream fos = new FileOutputStream("C:/Temp/file.txt");
Writer writer = new OutputStreamWriter(fos);
FileOutputStream
에 OutputStreamWriter
를 연결하지 않고 FileWriter
를 직접 생성할 수도 있다. (FileInputStream
, FileReader
와 같은 원리)프로그램의 실행 성능은 입출력이 가장 늦은 장치를 따라간다.
이 문제에 대한 완전한 해결책은 될 수는 없지만, 프로그램이 입출력 소스와 직접 작업하지 않고 중간에
메모리 버퍼(buffer)
와 작업함으로써 실행 성능을 향상시킬 수 있다.
예를 들어 프로그램은 직접 하드 디스크에 데이터를 보내지 않고 메모리 버퍼에 데이터를 보냄으로써 쓰기 속도가 향상된다. 버퍼는 데이터가 쌓이기를 기다렸다가 꽉 차게 되면 데이터를 한꺼번에 하드 디스크로 보냄으로써 출력 횟수를 줄여준다.
보조 스트림 중에서는 위와 같이 메모리 버퍼를 제공하여 프로그램의 실행 성능을 향상시키는 것들이 있다.
- 바이트 기반 스트림에는
BufferedInputStream
,BufferedOutputStream
이 있고,- 문자 기반 스트림에는
BufferedReader
,BufferedWriter
가 있다.
BufferedInputStream
: 바이트 입력 스트림에 연결되어 버퍼를 제공해주는 보조 스트림BufferedReader
: 문자 입력 스트림에 연결되어 버퍼를 제공해주는 보조 스트림
BufferedInputStream
과 BufferReader
는 입력 소스로부터 자신의 내부 버퍼 크기만큼 데이터를 미리 읽고 버퍼에 저장해 둔다. 프로그램은 외부의 입력 소스로부터 직접 읽는 대신 버퍼로부터 읽음으로써 읽기 성능이 향상된다. BufferedInputStream
과 BufferedReader
보조 스트림은 다음과 같이 생성자의 매개값으로 준 입력 스트림과 연결되어 8192 내부 버퍼 사이즈를 가지게 된다. BufferedInputStream
은 최대 8192 바이트가, BufferedReader
는 최대 8192 문자가 저장될 수 있다.BufferedInputStream bis = new BufferedInputStream(바이트입력스트림);
BufferedReader br = new BufferedReader(문자입력스트림);
BufferedInputStream
과 BufferedReader
로 데이터를 읽어들이는 방법은 InputStream
또는 Reader
와 동일하다. BufferedReader
는 readLine()
메소드를 추가적으로 가지고 있다.\r
) 라인피드(\n
)로 구분된 행 단위의 문자열을 한꺼번에 읽을 수 있다. Enter키
를 입력하기 전까지 콘솔에서 입력한 모든 문자열을 한꺼번에 읽는다.Reader reader = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(reader);
String inputStre = br.readLine();
BufferedOutputStream
: 바이트 출력 스트림에 연결되어 버퍼를 제공해주는 보조 스트림BufferedWriter
: 문자 출력 스트림에 연결되어 버퍼를 제공해주는 보조 스트림
BufferedOutputStream
과 BufferedWriter
는 프로그램에서 전송한 데이터를 내부 버퍼에 쌓아두었다가 버퍼가 꽉 차면, 버퍼의 모든 데이터를 한꺼번에 보낸다. 프로그램 입장에서 보면 직접 데이터를 보내는 것이 아니라, 메모리 버퍼로 데이터를 고속 전송하기 때문에 실행 성능이 향상되는 효과를 얻게 된다.BufferedOutputStream
과 BufferedWriter
보조 스트림은 다음과 같이 생성자의 매개값으로 준 출력 스트림과 연결되어 8192 내부 버퍼 사이즈를 가지게 된다. BufferedOutputStream
은 8192 바이트가, BufferedWriter
는 8192 문자가 최대 저장될 수 있다.BufferedOutputStream bos = new BufferedOutputStream(바이트출력스트림);
BufferedWriter bw = new BufferedWriter(문자출력스트림);
BufferedOutputStream
과 BufferedWriter
로 데이터를 출력하는 방법은 OutputStream
또는 Writer
와 동일하다. ⭐ 버퍼가 가득 찼을 때만 출력을 하기 때문에 마지막 자투리 데이터 부분이 목적지로 가지 못하고 버퍼에 남아있을 수 있다. 그래서 마지막 출력 작업을 마친 후에는
flush()
메소드를 호출하여 버퍼에서 잔류하고 있는 데이터를 모두 보내도록 해야 한다.
DataInputStream
과 DataOutputStream
보조 스트림을 연결하면 기본 데이터 타입으로 입출력이 가능하다.DataInputStream dis = new DataInputStream(바이트입력스트림);
DataOutputStream dos = new DataOutputStream(바이트출력스트림);
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(문자출력스트림);
print()
메소드는 출력할 데이터 끝에 개행 문자인 '\n'
을 더 추가시켜주기 때문에 콘솔이나 파일에서 줄 바꿈이 일어나지만, print()
메소드는 개행 없이 계속해서 문자를 출력시킨다.printf()
메소드는 형식화된 문자열(format string
)을 출력할 수 있도록 하기 위해 자바 5부터 추가된 메소드이다.%
와 conversion(변환문자)
은 필수적으로 작성하고 그 외의 항목은 생략할 수 있다.argument_index$
는 적용할 매개값의 순번인데 1$
는 첫 번째 매개값, 2$
는 두 번째 매개값을 말한다.flags
는 빈공간을 채우는 방법인데, 생략되면 왼쪽이 공백으로 채워지고, -
는 오른쪽이 공백으로 채워진다. 0
은 왼쪽이 0
으로 채워진다.width
는 전체 자릿수이며, .precision
은 소수자릿수를 뜻한다.정수(d)
, 실수(f)
, 문자열(s)
과 시간과 관련된 문자가 와서 매개값을 해당 타입으로 출력한다.형식화된 문자열에서 자주 사용되는 것을 정리해보면 다음과 같다.
자바는 메모리에 생성된 객체를 파일 또는 네트워크로 출력할 수 있다.
직렬화(serialization)
라고 한다. 역직렬화(deserialization)
라고 한다.ObjectInputStream
과 ObjectOutputStream
을 제공한다. ObjectOutputStream
은 바이트 출력 스트림과 연결되어 객체를 직렬화하는 역할을 하고, ObjectInputStream
은 바이트 입력 스트림과 연결되어 객체로 역직렬화하는 역할을 한다.ObjectInputStream ois = new ObjectInputStream(바이트입력스트림);
ObjectOutputStream oos = new ObjectOutputStrema(바이트출력스트림);
ObjectOutputStream
으로 객체를 직렬화하기 위해서는 writeObject()
메소드를 사용한다.oos.writeObject(객체);
ObjectInputStream
의 readObject()
메소드는 입력 스트림에서 읽은 바이트를 역직렬화해서 객체로 생성한다. readObject()
메소드의 리턴 타입은 Object
타입이기 때문에 객체 원래의 타입으로 변환해야 한다.객체타입 변수 = (객체 타입) ois.readObject();
자바는 Serialzable
인터페이스를 구현한 클래스만 직렬화할 수 있도록 제한하고 있다. Serializable
인터페이스는 필드나 메소드가 없는 빈 인터페이스이지만, 객체를 직렬화할 때 private
필드를 포함한 모든 필드를 바이트로 변환해도 좋다는 표시 역할을 한다.
public class XXX implements Serializable { }
객체를 직렬화하면 바이트로 변환되는 것은 필드들이고, 생성자 및 메소드는 직렬화에 포함되지 않는다. 따라서 역직렬화할 때에는 필드의 값만 복원된다. 하지만 모든 필드가 직렬화 대상이 되는 것은 아니다. 필드 선언에 static
또는 transient
가 붙어 있을 경우에는 직렬화가 되지 않는다.
public class XXX implements Serializable {
public int field1;
protected int field2;
int field3;
private int field4;
public static int field5; //직렬화에서 제외
transient int field6; //직렬화에서 제외
}
직렬화된 객체를 역직렬화할 때는 직렬화했을 때와 같은 클래스를 사용해야 한다.
java.io.InvalidException: XXX; local class incompatible: stream classdesc
serialVersionUID: -9130799490637378756, local class serialVersionUID = -1174725809595957294
serialVersionUDI
가 다르다는 것이다. serialVersionUID
는 같은 클래스임을 알려주는 식별자 역할을 하는데, Serializable
인터페이스를 구현한 클래스를 컴파일하면 자동적으로 serialVersionUID
정적 필드가 추가된다. serialVersionUID
의 값이 달라지기 때문에 문제가 발생한다. serialVersionUID
를 갖는 클래스를 가지고 있어야 하는데 한 쪽에서 클래스를 변경해서 재컴파일하면 다른 serialVersionUID
를 가지게 되므로 역직렬화에 실패하게 된다.serialVersionUID
가 명시적으로 선언되어 있으면 컴파일 시에 serialVersionUID
필드가 추가되지 않기 때문에 동일한 serialVersionUID
값을 유지할 수 있다. serialVersionUID
의 값은 개발자가 임의로 줄 수 있지만 가능하다면 클래스마다 다른 값을 갖도록 하는 것이 좋다.두 클래스가 상속 관계에 있다고 가정해보자. 부모 클래스가 Serializable
인터페이스를 구현하고 있으면 자식 클래스는 Serializable
인터페이스를 구현하지 않아도 자식 객체를 직렬화하면 부모 필드 및 자식 필드가 모두 직렬화된다. 하지만 그 반대로 부모 클래스가 Serializable
인터페이스를 구현하고 있지 않고, 자식 클래스만 Serializable
인터페이스를 구현하고 있다면 자식 객체를 직렬화할 때 부모의 필드는 직렬화에서 제외된다. 이 경우 부모 클래스의 필드를 직렬화하고 싶다면 다음 두 가지 방법 중 하나를 선택해야 한다.
- 부모 클래스가 Serializable 인터페이스를 구현하도록 해야 한다.
- 자식 클래스에서
writeObject()
와readObject()
메소드를 선언해서 부모 객체의 필드를 직접 출력시킨다.
첫 번째 방법이 제일 좋은 방법이 되겠지만, 부모 클래스의 소스를 수정할 수 없는 경우에는 두 번째 방법을 사용해야 한다. writeObject()
메소드는 직렬화될 때 자동으로 호출되고, readObject()
메소드는 역직렬화될 때 자동적으로 호출된다.
private void writeObject(ObjectOutputStream out) throws IOException {
out.writeXXX(부모필드); //부모 객체의 필드값을 출력함
...
out.defaultWriteObject(); //자식 객체의 필드값을 직렬화
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
부모필드 = in.readXXX(); //부모 객체의 필드값을 읽어옴
...
in.defaultReadObject(); //자식 객체의 필드값을 역직렬화
}
두 메소드를 작성할 때 주의할 점은 접근 제한자가 private
가 아니면 자동 호출되지 않기 때문에 반드시 private
을 붙여주어야 한다. writeObject()
와 readObject()
메소드의 매개값인 ObjectOutputStream
과 ObjectInputStream
은 다양한 종류의 writeXXX()
, readXXX()
메소드를 제공하므로 부모 필드 타입에 맞는 것을 선택해서 사용하면 된다. defaultWriteObejct()
와 defaultReadObject()
는 자식 클래스에 정의된 필드들을 모두 직렬화하고 역직렬화한다.
이것이 자바다 책