본문 바로가기
  • 1+1=3
개발삽질/SSAFY하는 동안 기록

[Java]보조스트림(InputStreamReader, BufferedReader, ObjectInputStream(직렬화) 등등)

by 여스 2022. 2. 6.
반응형

보조스트림은 기존 노드스트림에다가 확장해서 연결할 때 씀.

보조스트림 종류

- byte 스트림을 char로 스트림으로 변환

InputStreamReader, OutputStreamWriter

 

- 버퍼링을 통한 속도 향상

 - byte기반: BufferedInputStream, BufferedOutputStream

- char기반: BufferedReader, BufferedWriter

 

-  객체전송

ObjectInputStream

ObjectOutputStream

 

사용할 스트림 결정해보자

다음 순서로 결정하면 된다.

노드가 무엇인가? -> 타입은 문자열 or 바이트인가? -> 방향은? -> 추가기능은(보조스트림)?

 

  • 영화 파일을 빠른 속도로 이동시키고 싶다면?

File -> byte -> 읽기 -> FileInputStream -> BufferedInputStream

File -> byte -> 쓰기 -> FileOutputStream -> BufferedOutputStream

 

  • 키보드에서 유니코드 문자를 안전하고 빠르게 읽고 싶다면?

Keyboard -> byte -> 읽기 -> InputStream, System.in -> InputStreamReader -> BufferedReader

 

  • 메모리의 객체를 파일로 저장하고 싶다면?

File -> byte -> 쓰기 -> FileOutputStream -> ObjectOutputStream

 

 

BufferedInputStream 사용해보기

체크포인트:

다음처럼 써야 함. 노도스트림을 정하고, 이걸 넣어주는 거임.

보조스트림 = new 보조스트림(노드스트림);

public class 보조스트림 {

	public static void main(String[] args) {
		File src = new File("C:"+File.separator+"SSAFY"+File.separator+"ipsum.txt");
		File target = new File("C:"+File.separator+"SSAFY"+File.separator+"copy.txt");
		
		try(FileInputStream fi = new FileInputStream(src);
			FileOutputStream fo = new FileOutputStream(target);){
			copy("노드만 사용: ", fi, fo);
		}catch (Exception e) {
			// TODO: handle exception
		}
		
		try(BufferedInputStream fi = new BufferedInputStream(new FileInputStream(src));
			BufferedOutputStream fo = new BufferedOutputStream(new FileOutputStream(target));){
			copy("보조도 사용: ", fi, fo);
		}catch (Exception e) {
			// TODO: handle exception
		}
	}
	
	public static void  copy(String type, InputStream input, OutputStream output)throws IOException{
		long start = System.nanoTime();
		byte[] cart = new byte[1024];
		int read = -1;
		while((read = input.read(cart))>0) {
			output.write(cart, 0, read);
		}
		long end = System.nanoTime();
		System.out.println(type+",소유시간: "+(end-start) + "ns");
	}

}

출력결과:

=>

노드만 사용: ,소유시간: 245800ns
보조도 사용: ,소유시간: 109200ns

 

BufferedReader 사용해보기

기존의 일반 노드스트림만을 활용한 것:  scanner와 FileWriter, FileReader를 씀.

public class Diary {

	public static void main(String[] args) {
		Diary diary = new Diary();
		diary.writeDiary();
	}
	
	private void writeDiary() {
		File target = new File("C:"+File.separator+"SSAFY"+File.separator+"diary.txt");
		
		try(Scanner scanner = new Scanner(System.in);
			FileWriter writer = new FileWriter(target, true);
			FileReader reader = new FileReader(target);){
			
			System.out.println("일기를 작성합니다. 그만두려면 x를 입력하세요");
			String str = null;
			while(true) {
				str = scanner.nextLine();
				if(str.equals("x")) {
					break;
				}else {
					writer.write(str+"\n");
				}
			}
			
			char[] buffer = new char[10];
			int read = -1;
			while((read = reader.read(buffer))>0) {
				System.out.println(String.valueOf(buffer,0,read));
			}
			
		}catch (Exception e) {
			// TODO: handle exception
		}
	}
}

=> 개선해보자

체크포인트:

1. Scanner를 InputStreamReader로 바꿈. 여기서 InputStreamReader도 보조스트림인데, 키보드에서 오는 byte를 char로 바꿔주는 역할을 함(이름을 보면 각이 나옴. stream은 바이트하는앤데 그 뒤에 char을 읽는 reader가 붙었으니까 유추가능)

2. Scanner의 nextline()대신 BufferedReader의  readLine()을 사용하였음.

3. 기본적으로 Reader를 상속한 것이기에 FileReader나 BufferedReader나 모두 .read()를 동일하게 사용한다.

public class Diary {

	public static void main(String[] args) {
		Diary diary = new Diary();
		diary.writeDiary();
	}
	
	private void writeDiary() {
		File target = new File("C:"+File.separator+"SSAFY"+File.separator+"diary.txt");
		
		try(BufferedReader fromKeyBoard = new BufferedReader(new InputStreamReader(System.in));
			BufferedWriter writer = new BufferedWriter(new FileWriter(target, true));
			BufferedReader reader = new BufferedReader(new FileReader(target));){
			
			System.out.println("일기를 작성합니다. 그만두려면 x를 입력하세요");
			String str = null;
			while(true) {
				str = fromKeyBoard.readLine();
				if(str.equals("x")) {
					break;
				}else {
					writer.write(str+"\n");
				}
			}
			
			char[] buffer = new char[10];
			int read = -1;
			while((read = reader.read(buffer))>0) {
				System.out.println(String.valueOf(buffer,0,read));
			}
			
		}catch (Exception e) {
			// TODO: handle exception
		}
	}

}

 

객체 직렬화

메모리에 객체가 특정주소에 뭉텅이로 저장되어있지. 근데 그걸 쭈우우욱 펴서 늘어뜨려 놓는다는 것임. 그렇게 쭈우우욱 늘어뜨려놔야 네트워크로 전송이 된다는것임. Person객체에 name, age 등등이 있으면 name - age - hometown - job - height 이렇게 일렬로 연속적인 데이터로 변환한다.

 

단, 모든 객체가 직렬화가 되는 것이 아님. 

- Serializable 인터페이스를 구현하해야 함.

- 클래스의 모드 멤버가 Serializable 인터페이스를 구현해야 함.

- 직렬화에서 제외하려는 멤버는 transient 선언을 해야 함.

 

SerialVersionUID

클래스의 변경여부를 파악하기 위한 유일 키.

직렬화를 했다가 나중에 다시 역직렬화를 하려는데 클래스 구조가 바뀌었다면(속성 하나 더 추가된다든지..) UID가 바뀐다. 이러면 예외발생

즉, 직려화 할때의 UID랑 역직렬화할때의 UID가 다르면 예외가 발생한다.

- 직렬화되는 객체에 UID가 설정되어있지 않으면 컴파일러가 자동으로 생성해줌(그러니까 직렬화되는 객체에 serialVersionUID를 설정하지 않고 멤버를 변경해서 클래스 구조가 바뀌면 UID가 다르게 자동으로 생성되서 다시 역직렬화할 때 예외가 발생하는 것임)

- 따라서 직렬화되는 객체에 serialVersionUID를 설정하는 것이 권장됨

 

예제

Person객체 직렬화 해보자. 

체크포인트

1. 직렬화할 대상인 Person은 당연하고, 멤버변수로 오는 Address클래스도 무조건 implements Serializable 해야 한다.

2. 웬만하면 UID를 설정해줄 것

3. 보조스트림 = new 보조스트림(노드스트림) 

4. read()할 때  Object로 받았는데, 걍 바로 쓰지 않고 null체크하고 instanceof로 확인 후 사용

5. toString() 오버라이딩해서 객체 프린트되도록 해줌

public class ObjectStreamTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
//		write();
		read();
	}

	private static File target = new File("C:"+File.separator+"SSAFY"+File.separator+"objPerson.dat");
	
	private static void write() {
		Person person = new Person("홍길동", "pass123", "010-1234", "seoul");
		
		try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(target))){
			oos.writeObject(person);
			System.out.println("person 저장완료!");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}	
	}
	private static void read() {
		try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(target))){
			Object obj = ois.readObject();
			if(obj !=null && obj instanceof Person) {
				Person casted = (Person)obj;
				System.out.println(casted);
			}
		}catch (Exception e) {
			// TODO: handle exception
		}
		
	}
}

 

-직렬화 할 객체에 UID넣기

UID를 넣으면 속성이 달라져도 역직렬화 시 예외가 발생하지 않는다.

public class Person implements Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = -2514785375074402994L;
	
	private String id;
	private transient String pass;
	private Address addr;

	
	public Person(String id, String pass, String zipCode, String city) {
		this.id = id;
		this.pass = pass;
		this.addr = new Address(zipCode, city);
	}
	
	@Override
	public String toString() {
		return "[id=" + id + ", pass ="+pass+", addr="+addr+"]";
	}
}

Address 클래스

public class Address implements Serializable{
	private String zipCode;
	private String city;
	
	public Address(String zipCode, String city) {
		this.zipCode = zipCode;
		this.city = city;
	}
	
	public String toString() {
		return "Addres [zipCode="+zipCode+", city="+city+"]";
	}
}
반응형

댓글