Recent Posts
Recent Comments
Link
12-04 00:16
Today
Total
관리 메뉴

삶 가운데 남긴 기록 AACII.TISTORY.COM

입출력 보조 스트림 본문

DEV&OPS/Java

입출력 보조 스트림

ALEPH.GEM 2022. 5. 19. 16:19

문자 기반 스트림으로 변환

소스 스트림이 바이트기반 스트림(InputStream, OutputStream, FileInputStream, FileOutputStream)이면서 문자로 변환해야한다면 Reader와 Writer로 변환해야 합니다.

InputStreamReader

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;

public class InputStreaReaderEx {

	public static void main(String[] args) throws IOException {
		InputStream is = System.in;
		Reader reader = new InputStreamReader(is);
		int readCharNo;
		char[] cbuf = new char[100];
		while((readCharNo=reader.read(cbuf)) != -1) {
			String data = new String(cbuf, 0, readCharNo);
			System.out.println(data);
		}
		reader.close();
		//실행시 콘솔에서 한글을 입력해보십시오.
	}

}

OutputStreamWriter

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;

public class OutputStreamWriterEx {

	public static void main(String[] args) throws IOException {
		FileOutputStream fos = new FileOutputStream("d:/temp/file4.txt");
		Writer writer = new OutputStreamWriter(fos);
		String data = "바이트 스트림인 FileOuptStream을 문자 스트림으로 변환";
		writer.write(data);
		writer.flush();
		writer.close();
		System.out.println("파일 저장");
	}

}

 

성능 향상 보조 스트림

성능은 가장 느린 부품(장치)의 속도를 따라갑니다.

그래서 빠른 장치에서 느린 장치에 스트림을 전송(Store)할 때에는 버퍼(Buffer)를 두고 느린 장치에서 빠른 장치로 전송(Load)할 때에는 캐시(Cache)를 두어서 성능을 향상시킵니다.

 

버퍼 Reader

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;

public class BufferedEx {

	public static void main(String[] args) throws IOException {
		//콘솔로부터 라인 단위로 읽기
		InputStream is = System.in;
		Reader reader = new InputStreamReader(is);
		BufferedReader br = new BufferedReader(reader);
		System.out.print("입력> ");
		String lineString = br.readLine();
		System.out.println("출력>"+lineString);
		
	}

}

버퍼 Writer

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class BufferedWriterEx {

	public static void main(String[] args) throws IOException {
		FileInputStream fis = null;
		FileOutputStream fos = null;
		BufferedInputStream bis = null;
		BufferedOutputStream bos = null;
		
		int data = -1;
		long start = 0;
		long end = 0;
		
		fis = new FileInputStream("d:/temp/file1.txt");
		bis = new BufferedInputStream(fis);
		fos = new FileOutputStream("d:/temp/file5.txt");
		start = System.currentTimeMillis();
		while((data = bis.read()) != -1) {
			fos.write(data);	//버퍼 사용안하고 output stream 직접 write
		}
		fos.flush();
		end = System.currentTimeMillis();
		fos.close();
		bis.close();
		fis.close();
		System.out.println("버퍼 미사용: "+(end - start)+" 밀리초");
		
		fis = new FileInputStream("d:/temp/file1.txt");
		bis = new BufferedInputStream(fis);
		fos = new FileOutputStream("d:/temp/file5.txt");
		bos = new BufferedOutputStream(fos);
		start = System.currentTimeMillis();
		while((data = bis.read()) != -1) {
			bos.write(data);	//버퍼 사용하여 write
		}
		bos.flush();
		end = System.currentTimeMillis();
		bos.close();
		fos.close();
		bis.close();
		fis.close();
		System.out.println("버퍼 사용: "+(end - start)+" 밀리초");
	}

}

 

기본 데이터 타입 입출력 보조 스트림

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

public class DataStreamEx {

	public static void main(String[] args) throws IOException {
		FileOutputStream fos = new FileOutputStream("d:/temp/primitive.dat");
		DataOutputStream dos = new DataOutputStream(fos);
		
		dos.writeUTF("홍길동");
		dos.writeDouble(95.1);
		dos.writeInt(0);
		
		dos.writeUTF("김길동");
		dos.writeDouble(90.2);
		dos.writeInt(3);
		
		dos.flush();	//버퍼를 비우고
		dos.close();	//보조스트림을 닫고
		fos.close();	//메인 스트림을 닫습니다.
		
		FileInputStream fis = new FileInputStream("d:/temp/primitive.dat");
		DataInputStream dis = new DataInputStream(fis);
		
		for(int i =0; i<2; i++) {
			String name = dis.readUTF();
			double score = dis.readDouble();
			int order = dis.readInt();
			System.out.println(name + " : " + score + " : " + order);
		}
		dis.close(); fis.close();
	}

}

 

PrintStream

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;

public class PrintStreamEx {

	public static void main(String[] args) throws IOException {
		FileOutputStream fos = new FileOutputStream("d:/temp/print.txt");
		PrintStream ps = new PrintStream(fos);
		ps.println("프린트 보조 스트림");
		ps.print("System.out.println 도");
		ps.println("일종의 프린트 스트림이죠.");
		ps.flush();
		ps.close();
		fos.close();
	}

}

 

객체 입출력 보조 스트림

자바의 객체를 파일 또는 네트워크로 출력할 수 있습니다.

객체는 문자가 아니기 때문에 바이트 기반 스트림으로 출력해야 합니다.

객체를 출력하기 위해서는 데이터(필드,멤버)들을 연속된 바이트로 변경해야 하는데 이를 직렬화(serialization)이라고 합니다.

반대로 연속된 바이트를 다시 객체로 복원하는 것을 역직렬화(deserialization)이라고 합니다.

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class ObjectStreamEx {

	public static void main(String[] args) throws IOException, ClassNotFoundException {
		FileOutputStream fos = new FileOutputStream("d:/temp/object.dat");
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		
		oos.writeObject(new Integer(11));
		oos.writeObject(new Double(2.718));
		oos.writeObject(new int[] {1,2,3,4,5});
		oos.writeObject(new String("java"));
		oos.flush();
		oos.close();
		fos.close();
		
		FileInputStream fis = new FileInputStream("d:/temp/object.dat");
		ObjectInputStream ois = new ObjectInputStream(fis);
		
		Integer obj1 = (Integer) ois.readObject();
		Double obj2 = (Double) ois.readObject();
		int[] obj3 = (int[]) ois.readObject();
		String obj4 = (String) ois.readObject();
		ois.close(); fis.close();
		
		System.out.println(obj1);
		System.out.println(obj2);
		System.out.println(obj3[0] + ","+ obj3[1] );
		System.out.println(obj4);
	}

}

 

Serializable

자바는 직렬화를 하려면 Serializable 인터페이스를 구현한 클래스만 직렬화 할 수 있습니다.

필드(멤버) 선언시 transient(직렬화 제외) 키워드가 있는 필드는 직렬화에서 제외 됩니다.

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

class ClassA implements Serializable{
	private static final long serialVersionUID = 1L;
	int field1;
	String field2;
	static int field3; 		
	transient int field4;	//직렬화 제외
}

public class SerializableWriter {

	public static void main(String[] args) throws IOException, ClassNotFoundException {
		//객체 정보를 파일로 저장
		FileOutputStream fos = new FileOutputStream("d:/temp/object.dat");
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		ClassA classA = new ClassA();
		classA.field1 = 10;
		classA.field2 = "test";
		classA.field3 = 3;
		classA.field4 = 4;
		oos.writeObject(classA);
		oos.flush();
		oos.close();
		fos.close();
		
		//파일로 저장된 객체정보를 복원
		FileInputStream fis = new FileInputStream("d:/temp/object.dat");
		ObjectInputStream ois = new ObjectInputStream(fis);
		ClassA v = (ClassA) ois.readObject();
		System.out.println(v.field1);
		System.out.println(v.field2);
		System.out.println(v.field3);
		System.out.println(v.field4);
		ois.close();
		fis.close();
	}

}

 

Deserialize

역직렬화할 때는 직렬화했을 때와 같은 클래스를 사용해야 합니다.

클래스의 이름이 같더라도 클래스의 내용이 변경되면 역직렬화는 실패합니다.

직렬화할 때와 역직렬화할 때 사용한 클래스의 serialVersionUID가 다르기 때문입니다.

이 serialVersionUID가 같은 클래스임을 식별하게 해주는 ID역할을 합니다.

네트워크로 객체를 직렬화해서 전송하는 경우 보내는쪽과 받는 쪽 같은 serailVersionUID를 같는 클래스를 가지고 있어야 합니다.

어느 한쪽에서 클래스를 변경하여 재컴파일하면 그 쪽의 ID값이 변경되어 역직렬화에 실패하게 됩니다.

불가피하게 클래스의 수정이 필요하다면 위의 예제 처럼 

static final long serialVersionUID = 정수값; 

을 명시적으로 선언해야 합니다.

JDK\bin 경로에 serailVersionUID를 자동 생성해주는 serialver.exe 를 제공하고 있습니다.

 

상속과 직렬화

부모 클래스가 Serializable을 구현하고 있으면 자식 클래스는 Serializable을 구현하지 않아도 모두 직렬화 됩니다.

하지만 부모 클래스는 제외하고 자식 클래스만 Serializabel를 구현하고 있으면 부모의 필드(멤버)는 직렬화에서 제외됩니다.

부모 클래스를 수정할 수 없는 경우 자식 클래스에서 writeObject()와 readObject()를 선언해서 부모 객체의 필드를 직접 읽고 쓰기를 해야 합니다.

이 writeObject()는 직력화할 때 자동 호출되고 readObject()는 역직렬화할 때 자동 호출되기 때문입니다.

주의 할점은 위 메소드들은 private로 선언을 해야 한다는 점입니다.

 

Parent.java : Serializable을 구현하지 않은 클래스

//Serializable을 구현하지 않음
public class Parent {
	public String field1;
}

Child.java 

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class Child extends Parent implements Serializable{
	public String field2;
	
	private void writeObject(ObjectOutputStream out) throws IOException{
		out.writeUTF(field1);
		out.defaultWriteObject();
	}
	
	private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{
		field1 = in.readUTF();
		in.defaultReadObject();
	}
}

직렬화 및 역직렬화

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class ParentEx {

	public static void main(String[] args) throws IOException, ClassNotFoundException {
		FileOutputStream fos = new FileOutputStream("d:/temp/object.dat");
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		Child child = new Child();
		child.field1 = "홍길동";
		child.field2 = "고길동";
		oos.writeObject(child);
		oos.flush();
		oos.close();
		fos.close();
		
		FileInputStream fis = new FileInputStream("d:/temp/object.dat");
		ObjectInputStream ois = new ObjectInputStream(fis);
		Child v = (Child)ois.readObject();
		System.out.println(v.field1);
		System.out.println(v.field2);
		ois.close();
		fis.close();
		
	}

}

 

 

 

 

 

 

 

 

 

 

728x90

'DEV&OPS > Java' 카테고리의 다른 글

NIO Path  (0) 2022.05.23
IO(블로킹)기반 자바 네트워킹  (0) 2022.05.20
파일 입출력  (0) 2022.05.18
자바 콘솔 입출력  (0) 2022.05.17
자바 stream  (0) 2022.05.17