[Java] 2020.12.02. day_29 FileStream_출력스트림 , Object Stream_객체스트림

2020. 12. 2. 17:49Web_Back-end/Java SE

◎ 출력 스트림

- 출력의 목적지 : File
FileStream 사용
8bit FileOutputStream : 모든 데이터를 파일에 기록할 수 있다
16bit FileWriter : 문자열 데이터만 파일에 기록할 수 있다
---------------------------------------------
FileOutputStream
사용
1. 스트림 생성(연결) 같은 이름의 파일이 존재하면 덮어쓰고, 같은 이름의 파일이 없다면 파일만 생성
(폴더는 생성하지 못한다)

FileOutputStream fos = new FileOutputStream(new File("파일경로"));
2. 데이터를 스트림쓴다 fos.write( 10 ); //int나 byte만 가능, 문자열불가

스트림(관) 앞에 생성되었음(목적지로 도달x)
3. 스트림에 기록된 내용을
목적지분출한다
fos.flush();

기본형은 flush를 하지 않아도 목적지로 분출되고,
참조형은 반드시 flush를 해야 목적지로 분출된다
4. 기록된다 -
5. 스트림의 연결끊기 fos.close();
가장중요. flush를 하지 않아도 close를 하면 stream에 내용이 분출되고 연결이 끊어진다
-------------------------------------------
FileWriter
사용 //문자열 데이터만 파일에 기록
16bit stream 단점 : 느리다

속도향상을 위해 BufferedWriter + FileWriter 이렇게도 사용한다
1. 스트림 생성(연결) 같은 이름의 파일이 존재하면 덮어쓰고, 같은 이름의 파일이 없다면 파일만 생성
(폴더는 생성하지 못한다)

FileWriter fw = new FileWriter(new File("파일경로"));

속도향상)
BufferedWriter fw = new BufferedWriter(new FileWriter(new File("파일경로")));
2. 데이터를 스트림 쓴다 fw.write( "문자열" ); //문자열

스트림(관) 앞에 생성되었음(목적지로 도달x)
3. 스트림에 기록된 내용을
목적지로 분출한다
fw.flush();

기본형은 flush를 하지 않아도 목적지로 분출되고, 
참조형은 반드시 flush를 해야 목적지로 분출된다
4. 기록된다 -
5. 스트림의 연결끊기 fw.close();
가장중요.flush를 하지 않아도 close를 하면 stream에 내용이 분출되고 연결이 끊어진다
------------------------------------------------
속도향상을 위해 8,16bit stream 복합사용

BufferedWriter(속도향상을 위해) FileOutputStream(파일에 연결,내용기록)
+ OutputStreamWriter (8+16 연결)
1. 스트림 생성(연결) 같은 이름의 파일이 존재하면 덮어쓰고, 같은 이름의 파일이 없다면 파일만 생성
(폴더는 생성하지 못한다)

BufferedWriter bw= new BifferedWriter(new OutputStreamWriter(new FileOutputStream(new File("파일경로"))));
2. 데이터를 스트림 쓴다 bw.write( "문자열" ); //문자열

스트림(관) 앞에 생성되었음(목적지로 도달x)
3. 스트림에 기록된 내용을
목적지로 분출한다
bw.flush();

기본형은 flush를 하지 않아도 목적지로 분출되고, 
참조형은 반드시 flush를 해야 목적지로 분출된다
4. 기록된다 -
5. 스트림의 연결끊기 bw.close();
가장중요.flush를 하지 않아도 close를 하면 stream에 내용이 분출되고 연결이 끊어진다

○ 파일 복사

  • 파일 복사 : 대상 파일을 읽어 들여, 다른 이름의 파일에 그대로 내보내는 것
- 파일복사
1. 읽기 스트림 연결 FileInputStream fis = new FileInputStream(new File("경로"));
2. 쓰기 스트림 연결 FileOutputStream fos = new FileOutputStream(new File("경로"));
3. 파일의 내용을 읽어와서 int temp = 0;
while( (temp = fis.read()) != -1){ //read() : 한바이트씩 읽어서 반환한다
4. 스트림 기록   fos.write( temp );
5. 스트림의 내용을 목적지로 분출   fos.flush();
}
6. 연결끊기 fos.close();
fis.close();

 

더보기
package day2202;

import java.awt.FileDialog;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.border.TitledBorder;

/**
 * 버튼을 클릭하면 FileDialog를 제공하고, FileDialog에서 파일을 선택하면<br>
 * 선택한 파일의 내용을 복사한 후 결과를 JTextArea에 출력
 * 
 * @author owner
 */
@SuppressWarnings("serial")
public class FileCopy extends JFrame implements ActionListener {

	public static final int EOF = -1;
	private JButton jbtnFileOpen;
	private JTextArea jtaFileList;

	public FileCopy() {
		super("파일 복사");

		jbtnFileOpen = new JButton("파일선택");
		jtaFileList = new JTextArea();

		JScrollPane jspNote = new JScrollPane(jtaFileList);
		jspNote.setBorder(new TitledBorder("복사 파일 목록"));

		JPanel jpNorth = new JPanel();
		jpNorth.add(jbtnFileOpen);

		// 이벤트 등록
		jbtnFileOpen.addActionListener(this);

		add("North", jpNorth);
		add("Center", jspNote);

		setBounds(300, 300, 500, 500);
		setVisible(true);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}

	@Override
	public void actionPerformed(ActionEvent ae) {
		if (ae.getSource() == jbtnFileOpen) {
			try {
				fileCopy();
			} catch (IOException e) {
				JOptionPane.showMessageDialog(this, "파일복사중 문제발생");
				e.printStackTrace();
			}
		}
	}

	private void fileCopy() throws IOException {
		FileDialog fdOpen = new FileDialog(this, "파일선택", FileDialog.LOAD);
		fdOpen.setVisible(true);

		String path = fdOpen.getDirectory();
		String name = fdOpen.getFile();

		if (path != null) {// 파일을 선택하고 열기를 누른경우
			File originalFile = new File(path + name);

			if (originalFile.exists()) {// 파일이 존재한다면

				StringBuilder sbCopyName = new StringBuilder(originalFile.getAbsolutePath());
				// 파일명에 _bak 를 넣어 복사할 파일명 생성
				sbCopyName.insert(sbCopyName.lastIndexOf("."), "_bak");

				File copyFile = new File(sbCopyName.toString());

				boolean copyFlag = false;
				if (copyFile.exists()) {// 복사할 파일명과 동일한 파일명 존재?
					switch (JOptionPane.showConfirmDialog(this, copyFile.getName() 
							+ "동일 파일명이 존재합니다. \n덮어쓰시겠습니까?")) {
					case JOptionPane.OK_OPTION:
						copyFlag = false;
						break;
					case JOptionPane.NO_OPTION:
						copyFlag = true;
						break;
					case JOptionPane.CANCEL_OPTION:
						copyFlag = true;
						break;
					}

				}

				if (!copyFlag) {//중복파일이 없으면 false, 예=false, 아니오&취소=true
					FileInputStream readStream = null;
					FileOutputStream writeStream = null;

					try {
						// 1. 파일을 읽기 위한 스트림 연결
						readStream = new FileInputStream(originalFile);
						// 2. 파일을 쓰기 위한 스트림 연결
						writeStream = new FileOutputStream(copyFile);

						// 3. 파일의 내용을 읽어와서
						int temp = 0; // temp : 읽어들이는 바이트를 담는다
						while ((temp = readStream.read()) != EOF) {// EOF : 파일의 끝 상수 -1 만들었음
							// 4. 스트림기록
							writeStream.write(temp); // 원본의 정보를 읽어들여, 카피본에 담는다
							// 5. 스트림의 내용을 목적지로 분출
							writeStream.flush(); // copyFile에 분출한다
						}
						JOptionPane.showMessageDialog(this, name + "파일이 복사되었습니다");
					} finally {
						// 6. 연결끊기
						if (readStream != null) {
							readStream.close();
						}
						if (writeStream != null) {
							writeStream.close();
						}

					}
					jtaFileList.append(sbCopyName.toString() + "\n");
				}
			}
		}
	}

	public static void main(String[] args) {
		new FileCopy();
	}

}

○ Java에서 (사이즈가 큰) HDD파일 읽고 쓰는 속도 개선하는 방법 

- Java에서 (사이즈가 큰) HDD파일 읽고 쓰는 속도 개선하는 방법
- 읽기, 쓰기 속도 개선
1. 읽기 스트림 연결 FileInputStream fis = new FileInputStream(new File("경로"));
2. 쓰기 스트림 연결 FileOutputStream fos = new FileOutputStream(new File("경로"));
3. 파일의 내용을 읽어와서 int readSize = 0; //읽어들인 크기

//HDD의 head가 한번에 읽어들여 저장한 크기와 같게(비슷한) 배열 생성
byte[] readData = new byte[512]; 

while( (readSize = fis.read(readData)) != -1 ){
//스트림에서 읽어들인 데이터를 fis.read( )
//입력받은 배열에 넣고 readData
//배열의 값이 있는 방의 개수를 저장 readSize
4. 스트림 기록   fos.write( readData, 0, readSize );
//readData에서 0번째부터 readSize(읽어들인 사이즈)까지
위의 소스코드에서
이 부분을
오른쪽이미지처럼 수정하면
HDD를 읽어들이는 속도가
개선된다


◎ ObjectStream

- ObjectStream (객체스트림) == MarshallStream
ObjectStream
==
MarshallStream
instance를 JVM외부로 내보내거나,
JVM 외부에 존재하는 instance를 JVM내부로 읽어들일 때 사용
모든 객체는 Stream 을 타고 JVM 외부로 나갈 수 없다 == 사이즈를 모르기 때문
기본형 데이터형은 Stream 을 타고 JVM 외부로 나갈 수 있다
transient : 직렬화방지키워드로 막을 수 있음
객체를 읽을 때 ObjectInputStream 사용
객체를 내보낼 때 ObjectOuputStream 사용
객체가 Stream을 타고 JVM외부로 나가려면,
java.io.Serializable 인터페이스를 구현해야한다
직렬화 하지 않는다면, NotSerializableException 발생
Serializable interface를 구현한 클래스는 Stream을 타고 나갈 수 있게 
일정크기로 쪼개진다 (직렬화 == Marshalling) ==> 사이즈가 정해지는 것과 비슷
쪼개진 객체를 원래의 상태로 만드는 것 (역직렬화 == UnMarshalling)
transient
(day_7 참조)
객체스트림의 사용
JVM 내부의 직렬화된 객체를 JVM 외부로 출력하기
1. 직렬화가 가능한 객체 생성 public class Test implements Serializable {
   private transient int i;
//Serializable 구현해도 transient 로 지정한 값은 JVM외부로 나갈 수 없다
//서로 상충되는 의미(역할)를 가진다
   private String str;
}
2. 객체를 내보내는 스트림 생성 ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("파일명") ); //어디까지 내보낼 것인지 명시해야함
3. 객체를 스트림에 기록 Test t = new Test();
oos.writeObject( t ); //직렬화가 되어 스트림에 기록
4. 스트림에 직렬화되어 저장된
객체를 목적지로 분출
oos.flush();
5. 스트림연결 끊기 oos.close();
JVM 외부의 직렬화된 객체를 JVM 내부로 읽어들이기
1. 객체 스트림 사용 ObjectInputStream ois = new ObjectInputStream( new FileInputStream("경로"));
2. 객체 읽기
조각난 객체가 합쳐져서 반환
== 역직렬화
ois.readObject(); //Object를 반환

클래스명 객체명 = (클래스명)ois.readObject();
//ClassNotFoundException  발생
3. 객체 사용 객체명.변수명
객체명.method명()
,,,