보조 스트림

dev_hnbm·2023년 11월 14일
0

대덕인재개발원

목록 보기
15/30

◾ 보조 스트림

보조 스트림을 사용하는 이유

1. 성능 향상

  • 입출력은 상대적으로 느린 작업이며, 매번 바이트 또는 문자를 하나씩 파일이나 네트워크에 쓰거나 읽는 것은 비효율적이다.
  • 따라서 여러 바이트를 한번에 처리하게 되면 효율성이 향상된다.
  • BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter 등이 해당된다.

2. 기능 추가

  • 기본 스트림에 기능을 추가하는데 사용된다.
    • DataInputStreamDataOutputStream은 기본 자료형을 바로 읽거나 쓸 수 있는 메소드 제공
    • ObjectInputStreamObjectOutputStream은 객체 직렬화를 지원하여 객체를 파일에 쓰거나 읽을 수 있음

3. 편리한 메소드 제공

  • 특정 작업을 수행하기 위한 메소드를 제공한다.
    • PrintWriter는 문자열을 쓰거나 println 메소드를 통해 자동으로 개행 추가

4. 코드 간결성

  • 코드가 간결해지고, 가독성이 좋아지며 필요한 기능이나 효율적인 작업을 보조 스트림이 대신 처리함으로 코드의 복잡성을 줄인다.


  • BufferedOutputStream은 내부적으로 데이터를 메모리에 버퍼링하고, 버퍼가 가득차거나 flush() 메소드가 호출될 때만 실제로 파일에 데이터를 쓰기 때문에 입출력 성능이 향상된다.

BufferedOutputStream 예제

try {
  FileOutputStream fos = new FileOutputStream("d:/d_other/bufferTest.txt");
  
  // 버퍼 크기가 5byte인 버퍼 스트림 객체 생성
  BufferedOutputStream bos = new BufferedOutputStream(fos, 5);
  
  for(char c='1'; c<='9'; c++) {
    bos.write(c);
  }
  
  // 출력 작업이 완료되면 출력 버퍼에 남아있는 데이터 강제 출력
  // bos.flush(); -> 얘 안 하면 5까지 출력
  
  // 보조 스트림을 닫으면 기반이 되는 스트림도 같이 닫힌다
  // 버퍼 스트림의 close()에는 flush() 기능이 내장되어 있다
  // fos.close();
  bos.close();
  
  System.out.println("작업 끝~~");
} catch (IOException e) {}



BufferedReader 예제

try {
  // 자료 읽을시 줄 단위로 읽을 수 있음
  FileReader fr = new FileReader("./src/kr/or/ddit/basic/FileTest01.Java"); // 해당 경로에 있는 자바 파일
  BufferedReader br = new BufferedReader(fr);
  
  String temp = "";
  
  for(int i=1; (temp = br.readLine()) != null; i++) {
    System.out.printf("%4d : %s\n", i, temp);
  }
  
  br.close();
} catch (IOException e) {}



DataInputStream, DataOutputStream

  • 데이터를 기본형 단위로 읽고 쓰는 보조스트림

  • 메소드 💁‍♀️
    • readInt(), readByte(), readChar()..
      • 8개의 기본 타입에 맞춰진 메소드
      • 각 자료형에 맞는 값들을 읽어옴
      • ex) int, long, short 입력시 내가 저장한 순서 4byte, 8byte, 2byte로 읽어야 함
    • String readUTF(): UTF 형식으로 쓰여진 문자를 읽음
    • writeInt(), writeByte(), writeChar()..
      • 8개의 기본 타입에 맞춰진 메소드
      • 각 자료형에 맞는 값들을 출력
    • void writeUTF(String s): UTF 형식으로 문자 출력

예제

// DataIOTest01.Java, DataIOTest02.Java
package kr.or.ddit.basic;

import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class DataIOTest01 {

	public static void main(String[] args) {
		try {
			// 파일 저장하기 위한 기반 스트림
			FileOutputStream fout = new FileOutputStream("d:/d_other/test.dat");
			
			// 자료형 단위로 출력할 보조 스트림 객체 생성
			DataOutputStream dout = new DataOutputStream(fout);
			
			// 출력
			dout.writeInt(200);
			dout.writeFloat(123.45f);
			dout.writeBoolean(false);
			dout.writeUTF("ABCDabcd");
			 
			System.out.println("출력 완료!!");
			
			dout.close();
			
		} catch (IOException e) {}

	}

}

// -------------------------------------------------------

package kr.or.ddit.basic;

import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class DataIOTest02 {

	public static void main(String[] args) {
		try {
			// DataOutputStream으로 출력했던 데이터 읽어오기
			FileInputStream fin = new FileInputStream("d:/d_other/test.dat");
			DataInputStream din = new DataInputStream(fin);
			
			// 출력 순서와 같은 순서로 읽기
			System.out.println("정수형: " + din.readInt());
			System.out.println("실수형: " + din.readFloat());
			System.out.println("논리형: " + din.readBoolean());
			System.out.println("문자열: " + din.readUTF());
			
			System.out.println("\n읽기 작업 끝!!");
			
			din.close();
			
		} catch (IOException e) {}

	}

}



ObjectInputStream, ObjectOutputStream

  • 객체를 직렬화하여 입출력할 수 있게 해주는 보조 스트림

  • 직렬화란? 객체의 멤버 변수를 연속적인 데이터로 변환하는 것이다.
    • Serializable인터페이스를 구현해야 객체를 직렬화할 수 있음
    • 구현 안 하면 예외 → NotSerializableException
      • Serializable인터페이스는 구현해야 할 메소드는 없다. 직렬화 하겠다는 표시임
      • 직렬화된 데이터를 생성하거나 읽어들일 수 있게 됨

  • 직렬화 제외 대상
    • transient가 붙은 인스턴스는 직렬화 대상에서 제외
    • Serializable을 구현하지 않은 클래스의 인스턴스도 직렬화 대상에서 제외
    • 자식 객체가 Serializable 인터페이스를 구현하더라도, 부모 객체가 Serializable을 구현하지 않은 경우, 부모 클래스의 멤버 변수는 자동으로 직렬화 대상에서 제외

예제

// ObjectIOTest.Java
package kr.or.ddit.basic;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class ObjectIOTest {

	public static void main(String[] args) {
		Member mem1 = new Member("홍길동", 20, "대전");
		Member mem2 = new Member("홍길서", 30, "서울");
		Member mem3 = new Member("홍길남", 40, "인천");
		Member mem4 = new Member("홍길북", 50, "천안");
		
		try {
			// 객체를 파일에 저장하기
			// 주로 처리할 보조 스트림을 마지막에 하는 게 좋당
			FileOutputStream fout = new FileOutputStream("d:/d_other/memObj.dat");
			BufferedOutputStream bout = new BufferedOutputStream(fout);
			ObjectOutputStream oout = new ObjectOutputStream(bout);
			
			// 저장 작업 (쓰기 작업)
			oout.writeObject(mem1);
			oout.writeObject(mem2);
			oout.writeObject(mem3);
			oout.writeObject(mem4);
			oout.writeObject(null); // EOFException 방지하기
			
			System.out.println("객체 저장 작업 끝!!");
			
			oout.close();
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		// ---------------------------------------------------------------------------
		
		// 저장된 객체를 읽어와 그 객체의 내용을 화면에 출력
		
		try {
			ObjectInputStream oin = new ObjectInputStream(
					new BufferedInputStream(
							new FileInputStream("d:/d_other/memObj.dat")));
			
			Object obj;
			
			System.out.println("객체 읽기 작업 시작..\n");
			System.out.println("------------------");
			
			// readObject(): 데이터를 끝까지 다 읽어오면 EOFException을 발생시킴
			while((obj = oin.readObject()) != null) {
				// 읽어온 데이터를 원래 자료형으로 형변환 후 사용
				Member mem = (Member)obj;
				
				
				System.out.println("이름: " + mem.getName());
				System.out.println("나이: " + mem.getAge());
				System.out.println("주소: " + mem.getAddr());
				System.out.println("------------------");
			}
			
			oin.close();
			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		
	}

}

class Member implements Serializable {
	
	// transient: 직렬화 되지 않을 멤버변수 지정
	private String name;
	private transient int age; // 나이가 0으로 출력
	private transient String addr;
	
	public Member(String name, int age, String addr) {
		super();
		this.name = name;
		this.age = age;
		this.addr = addr;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getAddr() {
		return addr;
	}
	public void setAddr(String addr) {
		this.addr = addr;
	}
	
	
}

0개의 댓글