목차
- 보조 스트림(filter stream)
- 보조 스트림 종류
2-1. 입출력 성능 향상 보조 스트림
2-2. 형변환 보조 스트림
2-3. 기본 데이터 타입 보조 스트림
2-4. 객체 입출력 보조 스트림
1. 보조 스트림(filter stream)
- java.io 패키지의 입출력 스트림은 기반 스트림(기본 스트림)과 필터 스트림(보조 스트림)으로 분류할 수 있다.
- 기반 스트림은 외부 데이터에 직접 연결되는 스트림인 반면, 필터 스트림은 외부 데이터에 직접 연결하는 것이 아니라 기반 스트림에 추가로 사용할 수 있는 스트림이다. 즉 필터 스트림은 실제 데이터를 주고 받는 스트림이 아니기 때문에 입출력 처리가 불가하다.
- 따라서 실제 데이터를 주고 받는 기반 스트림을 먼저 생성한 후 이용할 수 있다.
- 주로 성능을 향상시키거나 추가하기 위한 목적에서 사용된다.
FileInputStream fis = new FilesInputStream("sample.txt"); 기반 스트림 생성
BufferedInputStream bis = new BufferedInputStream(fis); 필터 스트림 생성
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("sample.txt")); 한 줄로 생성
bis.read(); 필터 스트림으로부터 데이터 읽기
- 생성자를 보면 기본 스트림과 필터 스트림간에 구분이 가능하다. 생성자 쪽에 매개변수로 다른 스트림을 이용하는 클래스는 필터 스트림이라고 볼 수 있다.
2. 보조 스트림 종류
2-1. 입출력 성능 향상 보조 스트림
- 버퍼(buffer)를 이용해 성능 향상시키는 보조 스트림을 말한다.
- BufferedInputStream/BufferedOutputStream과 BufferedReader/BufferedWriter가 있다.
- 버퍼를 이용하는 경우 버퍼가 가득 차면 자동으로 내보내기 되지만, 버퍼가 가득 차지 않은 상태에서는 강제로 내보내는 작업이 필요하다. 이때 그 기능을 수행하는 것이 flush() 메소드이다. try 블럭 안에 flush() 선언해야만 파일에 기록된다.
- finally 블럭은 항상 작성해 주는 것이 좋다. 이 안에서 close()를 호출하면 내부적으로 flush()를 하고 나서 자원을 반납한다. 따라서 별도로 flush() 작성하지 않고 close()만 잘 호출해주는 것도 방법이 될 수 있다.
BufferedWriter
BufferedWriter bw = null; 인스턴스 생성
try {
bw = new BufferedWriter(new FileWriter("src/filterstream/testBuffered.txt"));
bw.write("안녕하세요\n");
bw.write("반갑습니다\n");
bw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
BufferedReader
- 버퍼를 한 줄 단위로 읽어들이는 기능 readLine()을 제공한다. 이를 통해 기본 스트림보다 성능을 개선시킨다.
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("src/filterstream/testBuffered.txt"));
String temp;
while((temp = br.readLine()) != null) {
System.out.println(temp);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
BufferedInputStream
BufferedOutputStream
2-2. 형변환 보조 스트림
- 기본적으로 byte 단위로 동작하기에 문자로 읽어오기 위해서는 형변환이 필요하다.
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
- byte 기반 InputStream + char 기반 Reader 이름이 만나 InputStreamReader 타입으로 형변환 적용되었음을 알 수 있다.
- 즉 형변환 보조 스트림은 기본 스트림이 byte 기반 스트림이고, 보조 스트림이 char 기반 스트림인 경우에 사용한다.
System.in(InputStream) : 콘솔로부터 데이터를 입력 받는다.
System.out(OutputStream) : 콘솔에게 데이터를 출력한다.
System.err(PrintStream) : 콘솔에게 데이터를 출력한다.
- 위 목록은 표준 스트림에 해당된다. 자바에서는 콘솔이나 키보드 같은 표준 입출력 장치로부터 데이터를 입출력 하기 위한 스트림을 표준 스트림 형태로 제공하고 있다. 자주 사용되는 자원에 대해 미리 스트림을 생성해 두었기 때문에 개발자가 별도로 스트림을 생성하지 않아도 되는 것이다.
- System 클래스의 필드 in, out, err가 대상 데이터의 스트림을 의미한다.
InputStreamReader
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try {
System.out.print("문자열 입력 : ");
String value = br.readLine(); *bufferedReader.class에서 제공하는 메소드
System.out.println("value : " + value);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
- 결과적으로 Scanner와 같은 기능하는 것을 알 수 있다.
- 이런 표준 스트림 중 콘솔로부터 데이터를 읽어오는 기반 스트림은 InputStream이다.
- 이때 Buffer를 이용해서 성능을 향상시키고 싶어 InputStreamReader라는 형변환 보조 스트림을 사용한 예시이다.
OutputStreamWriter
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
try {
bw.write("java oracle jdbc");
} catch (IOException e) {
e.printStackTrace();
} finally {
if(br != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 출력을 위한 경우에는 OutputStreamWriter가 형변환 보조 스트림을 담당한다.
java oracle jdbc
- System.out 기반 스트림 따라 콘솔로 출력되는 것을 알 수 있다.
2-3. 기본 데이터 타입 보조 스트림
외부로부터 데이터를 가져올 때 byte형으로만 읽어온다면, 정수-문자-문자열 등 여러 데이터 타입을 취급하는 경우에는 별도로 처리해 주어야 하는 번거로움이 따른다.
예를 들면 정수를 입력 받아 파싱(parsing)해야 하는 것이다.
이때 데이터 자료형별로 처리하는 기능을 추가할 수 있는 보조 스트림이 바로 DataInputStream/DataOutputStream이다.
DataOutputStream
- 데이터 타입별로 파일에 기록하고 저장하기 위해 쓰인다.
DataOutputStream dout = null;
try {
dout = new DataOutputStream(new FileOutputStream("src/filterstream/score.txt"));
- 인스턴스를 생성한다.
dout.writeUTF("김영희");
dout.writeInt(95);
dout.writeChar('A');
dout.writeUTF("김철수");
dout.writeInt(87);
dout.writeChar('B');
dout.writeUTF("홍길동");
dout.writeInt(73);
dout.writeChar('C');
- 파일에 자료형별로 기록한다. writeInt(), writeChar() 등등 자료형별로 메소드가 존재하며, String 타입은 writeUTF()를 쓴다.
- 이때 int형은 일부 byte만 해석되어서 _ W 처럼 다른 문자나 기호로 출력될 것이다.
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(dout != null) {
try {
dout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
DataInputStream
- 입력한 순서대로 출력해야만 한다.
- 파일에 기록한 순서대로 읽어들이지 않는 경우 에러 발생 또는 올바르지 않은 값을 읽어온다.
- int일 때는 read() != -1, String일 때는 readLine() != null로 조건을 달아 알아볼 수 있었지만, 아래처럼 여러 타입을 사용할 때는 예외처리 해주면 되는 것이다.
DataInputStream din = null;
try {
din = new DataInputStream(new FileInputStream("src/filterstream/score.txt"));
- 데이터형별로 읽어오는 DataInputStream 인스턴스 작성한다.
while(true) {
System.out.println(din.readUTF() + ", " + din.readInt() + ", " + din.readChar());
}
- 무한루프 while문으로 읽어들이게 되면, 파일에 더 이상 읽을 것이 없을 때 EOFException이 발생한다.
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (EOFException e) { *while문 정상실행 위해 추가
System.out.println("파일 읽기를 완료하였습니다.");
} catch (IOException e) {
e.printStackTrace();
} finally {
if(din != null) {
try {
din.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 따라서 catch 블럭에 EOFException을 핸들링하는 코드를 추가한다.
2-4. 객체 입출력 보조 스트림
public class MemberDTO implements java.io.Serializable {
- 객체로 입출력 하기 위해서는 반드시 직렬화 처리를 해야 한다.
- 직렬화 없이 실행한 경우 java.io.NotSerializableException이 발생한다. 직렬화 처리를 해주지 않아 발생하는 에러이다.
❗ 직렬화(객체 → byte) : 자바 시스템 내부에서 사용되는 객체 또는 데이터를 외부에서도 사용할 수 있도록 바이트(byte) 형태로 변환하는 기술을 말한다.
❗ 역직렬화(byte → 객체) : 반대로 바이트(byte)로 된 데이터를 다시 객체로 변환하는 기술을 역직렬화라고 한다.
private static final long serialVersionUID = 3049248567805675348L;
- implements 키워드와 함께 직렬화 선언하면 클래스 이름에 노란색 줄이 생긴다. 그때 add generated serial version ID를 클릭해주면 일련의 long타입 ID가 자동 생성된다.
- 직렬화 시의 id와 역직렬화 시의 id가 불일치하면 역직렬화에 실패하게 된다. 명시적으로 작성해두지 않으면 JVM이 자동 생성하게 되는데, 이때 수정사항이 발생하면 id도 변경된다. 따라서 명시적으로 작성해두는 것을 권장한다.
private transient double point;
- 직렬화에서 제외하고 싶은 필드의 경우 transient 키워드를 사용해 입출력 대상에서 열외시킨다.
❗ 보조 스트림이 꼭 1개는 아니다. 아래는 Buffered 보조 스트림도 함께 사용한 예시이다.
MemberDTO[] outputMembers = {
new MemberDTO("user01", "pass01", "김영희", "y@", 25, '여', 1250.7),
new MemberDTO("user02", "pass02", "김철수", "c@", 27, '남', 1221.6),
new MemberDTO("user03", "pass03", "유관순", "y@", 18, '여', 1234.3),
};
ObjectOutputStream objOut = null;
try {
objOut = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream("src/filterstream/testObjectStream.txt")));
for(int i=0; i < outputMembers.length; i++) {
objOut.writeObject(outputMembers[i]);
}
objOut.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(objOut != null) {
try {
objOut.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
MemberDTO[] inputMembers = new MemberDTO[3];
ObjectInputStream objIn = null;
try {
objIn = new ObjectInputStream(new BufferedInputStream(new FileInputStream("src/filterstream/testObjectStream.txt")));
for(int i=0; i < inputMembers.length; i++) {
inputMembers[i] = (MemberDTO) objIn.readObject();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (EOFException e) {
System.out.println("파일을 모두 읽어왔습니다.");
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if(objIn != null) {
try {
objIn.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
for (MemberDTO member : inputMembers) {
System.out.println(member);
}
'Java' 카테고리의 다른 글
[자바의 정석] Ch 7. OOP II 예제 응용 학습 (0) | 2022.01.22 |
---|---|
[자바의 정석] Ch 7. OOP II 연습문제 풀이 (0) | 2022.01.16 |
[JAVA/수업 과제 practice] HashMap 데이터 관리 프로그램 만들기 (0) | 2022.01.13 |
[자바의 정석] Ch 7. 객체 지향 프로그래밍 II 강의 메모 (0) | 2022.01.13 |
[JAVA] 14-1. 입출력 | 스트림 | InputStream-OutputStream | Reader-Writer (0) | 2022.01.12 |