💁‍♀️ 입출력(IO)이란,
Input과 Output의 약자로 컴퓨터 내부 또는 외부 장치와 프로그램 간의 데이터를 주고 받는 것
장치와 입출력을 위해서는 하드웨어 장치에 직접 접근이 필요한데 다양한 매체에 존재하는 데이터들을 사용하기 위해 입출력 데이터를 처리 할 공통적인 방법으로 스트림을 이용


💁‍♀️ 스트림(Stream)이란,
입출력 장치에서 데이터를 읽고 쓰기 위해서 자바에서 제공하는 클래스

  • 모든 스트림은 단방향이며 각각의 장치마다 연결할 수 있는 스트림 존재
  • 하나의 스트림으로 입출력을 동시에 수행할 수 없으므로 동시에 수행하려면 2개의 스트림이 필요

👀 Stream의 최상위 클래스 (추상 클래스)

📍 Stream 최상위 클래스의 종류

1) InputStream
2) OutputStream
3) Reader
4) Writer

1) InputStream (Byte)

read() : 입력 스트림으로부터 1바이트를 읽고 읽은 바이트 리턴
close() : 사용한 시스템 자원 반납 후 입력 스트림을 닫음

2) OutputStream (Byte)

write(int b) : 출력 스트림으로 1바이트를 보냄
flush() : 버퍼에 잔류하는 모든 바이트 출력
close() : 사용한 시스템 자원 반납 후 출력 스트림을 닫음

3) Reader (Char)

read() : 입력 스트림으로부터 1바이트를 읽고 읽은 바이트 리턴
close() : 사용한 시스템 자원 반납 후 입력 스트림을 닫음

4) Writer (Char)

write(int b) : 출력 스트림으로 1바이트를 보냄
write(String str) : 출력 스트림에 매개 값으로 주어진 문자열을 보냄
flush() : 버퍼에 잔류하는 모든 바이트 출력
close() : 사용한 시스템 자원 반납 후 출력 스트림을 닫음


👀 File Stream (기반 스트림 중 하나)

💁‍♀️ 기반 스트림이란,
기반 스트림(기본 스트림)은 외부 데이터에 직접 연결이 되는 스트림


📌 Ref. File Class

💁‍ File Class란,
파일 처리를 수행하는 대표적인 클래스
대상 파일에 대한 정보로 인스턴스를 생성하고 파일의 생성, 삭제 등의 처리를 수행하는 기능을 제공


// 파일 클래스를 이용해서 인스턴스를 생성
>>> 대상 파일이 존재하지 않아도 인스턴스를 생성할 수 있음
File file = new File("src/com/greedy/section01/file/test.txt"); >>> 아직 파일 생성X
try {
	// createNewFile()을 통해 파일을 생성할 수 있고 성공 실패 여부를 boolean으로 반환
	boolean createSuccess = file.createNewFile(); 
    >>> 처음에 Unhandled exception type IOException 에러 발생 
    >>> File 메소드가 Exceptionthrow 하고 있기 때문 
    >>> 따라서 try&catch로 처리해야함
		
	>>> 최초 실행 시 새롭게 파일이 만들어지므로 true가 반환되고 
    >>> 파일이 한번 생성되고 난 이후는 새롭게 파일을 만들지 않기 때문에 false를 반환
    
	System.out.println(createSuccess); 
    >>> 최초 실행 시 true 출력. 왼쪽 file 패키지를 Refresh하면 test.txt 파일이 생성되어있음
    
} catch (IOException e) {
	e.printStackTrace();
} 
		
// 생성한 파일의 정보
System.out.println("파일의 크기 : " + file.length() + "byte"); 
System.out.println("파일의 경로 : " + file.getPath());
System.out.println("현재 파일의 상위 경로 : " + file.getParent()); 
System.out.println("파일의 절대 경로 : " + file.getAbsolutePath());
>>> '절대 경로', 최상위 루트 위치부터의 경로를 의미
		
// 파일 삭제 : 삭제 후 성공 실패 여부를 boolean으로 반환
boolean deleteSuccess = file.delete();
System.out.println(deleteSuccess);
>>> 최초 실행 시 true 출력. 왼쪽 file 패키지를 Refresh하면 test.txt 파일이 사라져있음 

💻 Mini Console

true
파일의 크기 : 0byte
파일의 경로 : src\com\greedy\section01\file\test.txt
현재 파일의 상위 경로 : src\com\greedy\section01\file
파일의 절대 경로 : D:\java\workspace\joys_workspace_lecture\joyschap14-io\src\com\greedy\section01\file\test.txt
true

📍 File Stream의 종류

1) FileInputStream
2) FileOutputStream
3) FileReader
4) FileWriter

📌 Ref.

* 자바 프로그램과 연결되는 외부 데이터의 타입이 무엇인지는 클래스의 이름을 보고 유추 가능
InputStream/OutputStream, Reader/Writer를 빼고 남은 단어가 '외부 데이터의 타입'
* ex) FileInputStream : 외부 데이터인 File로부터 1바이트 단위로 데이터를 읽어오는 입력 스트림

1) FileInputStream

외부 데이터인 File로부터 1바이트 단위로 데이터를 읽어오는 입력 스트림

◼ Application Class

FileInputStream fin = null;
>>> finally 블럭을 위해 try 블럭 밖에서 null로 값 초기화
		
try {
	>>> 대상 파일이 존재하지 않는 경우 발생하는 FileNotFoundException에 대해 핸들링 해야함. try&catch 블럭 처리 
	>>> 인스턴스만 생성한 후 실행하면 FileNotFoundException이 발생하며,
	>>> 파일을 직접 생성한 후 실행하면 예외가 발생X. 스트림 인스턴스가 정상적으로 생성된 것
	fin =  new FileInputStream("src/com/greedy/section02/stream/testInputStream.txt"); 
		
	int value;
			
	>>> read()throws IOException이므로 add catch clause to surrounding try를 눌러서 catch 블럭 하단에 추가
	>>> read() : 파일에 기록된 값을 1byte씩 순차적으로 읽어서 반환하고 value 변수에 담음. 
    			 더 이상 읽어올 데이터가 없는 경우 -1 반환
	while((value = fin.read()) != -1) { >>> != -1 : while문을 빠져나오기 위한 장치
    									>>> read()는 더이상 반환할 것이 없을 때 -1을 반환하기 때문
				
		// 값을 정수로 읽어오기
		System.out.println(value);
				
		// 문자로 읽어오기
		System.out.println((char)value);
	}
>>> 1byte씩 읽어오는 것은 대부분 비효율적. byte 배열을 이용해서 한 번에 데이터를 읽어올 수 있음
			
	// 파일의 길이만큼의 byte 배열 만들기
	int fileSize = (int) new File("src/com/greedy/section02/stream/testInputStream.txt").length();
	byte[] bar = new byte[fileSize];
			
	>>> 생성해둔 byte 배열을 read() 메소드의 인자로 넣어주면 파일의 내용을 읽어 byte 배열에 기록
	fin.read(bar); // byte 배열을 인자로 전달
			
	for(int i = 0; i < bar.length; i++) {
		System.out.println((char)bar[i]);
	}
			
} catch (FileNotFoundException e) {
	e.printStackTrace(); >>> 출력 시점에 파일이 없을 경우, '지정된 파일을 찾을 수 없습니다.' 라는 오류가 뜸
} catch (IOException e) {
	e.printStackTrace();
} finally {
	>>> fin 인스턴스가 null이 아닌 경우 자원 반납을 해야함
	if(fin != null) {
		try {
			>>> 자원을 반납하는 경우에도 IOException을 핸들링 해야함
			>>> IOException : 이미 자원이 반납된 경우 발생하는 Exception
			fin.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

◼ testInputStream.txt File

abcde

💻 Mini Console

a
b
c
d
e

2) FileOutputStream

프로그램의 데이터를 외부 데이터인 File로 1바이트씩 내보내는 출력 스트림

◼ Application Class

FileOutputStream fos = null;
		
try {
	>>> FileNotFoundException을 핸들링 해야하는 것은 입력 스트림과 동일하지만,
    >>> 파일이 없는 상태에서 실행해도 예외가 발생하지않고 인스턴스가 잘 생성됨
	>>> '출력 스트림'의 경우, 대상 파일이 존재하지 않으면 파일을 자동으로 생성해줌 (예외 발생 X) 
	>>> But, FileNotFoundException이 핸들링 하는 부분은 경로. 존재하지 않는 경로까지 만들어주지는 않음
			
    // 파일과 연결되는 fos 객체 생성
	fos = new FileOutputStream("src/com/greedy/section02/stream/testOuputStream.txt", *true); 
    >>> 실행을 할 때마다 testOuputStream.txt에 아래의 문자들이 계속 추가(이어쓰기)>>> write 메소드는 IOException을 핸들링 해야함
	fos.write(97);
			
	// byte 배열을 이용해서 한 번에 기록하기 (10 = 개행문자)
	byte[] bar = new byte[] {98, 99, 100, 101, 102, 10, 103, 104, 105};
	fos.write(bar);
			
	// bar 배열의 1번 인덱스부터 3의 길이만큼 파일에 내보내기
	fos.write(bar, 1, 3); // 100, 101, 102 -> cde 출력
			
} catch (FileNotFoundException e) {
	>>> 경로의 범위가 잘못되면 예외('지정된 경로를 찾을 수 없습니다')가 발생하지만, 
    >>> 경로는 맞지만 파일이 없는 경우는 에러가 발생되지 않고 파일을 만들어줌
	e.printStackTrace();
} catch (IOException e) { // write()을 위한 예외처리
	e.printStackTrace();
} finally { // 다 사용하고나서 자원 반납
	if(fos != null) {
		try {
			fos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

📌 Ref.

* true : 생성자의 두 번째 인자로 true를 전달하면 파일 '이어쓰기', 
  전달하지 않으면(or false 전달) 파일 '덮어쓰기'

◼ testOuputStream.txt File

abcdef
ghicdeabcdef
ghicdeabcdef
ghicde

//	true로 인해, 실행할 때 마다
//	abcdef
//	ghicde 가 계속 추가됨

3) FileReader

외부 데이터 File로부터 문자 단위로 데이터를 읽어오는 입력 스트림

: FileInputStream과 사용하는 방법이 거의 동일하나, byte 단위가 아닌 character단위로 읽어들이는 부분이 차이

◼ Application Class

FileReader fr = null;
		
try {
	>>> 파일이 존재하지 않는 경우 FileNotFoundException이 발생하므로 파일을 추가해서 
    >>> 정상적으로 스트림이 생성되도록 함
	fr = new FileReader("src/com/greedy/section02/stream/testReader.txt");
			
	int value;
    >>> 모든 문자는 그와 대응되는 숫자가 있기 때문에 int 타입으로 값을 저장
    
	while((value = fr.read()) != -1) {
    >>> read()IOException에 대한 처리가 필요함
    	// 문자 단위로 파일을 읽어옴
		System.out.println((char)value);
	} 						
>>> byte가 아닌 char 배열로 읽어오는 기능을 제공
    
    // 해당 파일의 길이만큼의 carr 배열 생성
	char[] carr = new char[(int) new File("src/com/greedy/section02/stream/testReader.txt").length()]; 
    
    // read()로 배열 읽음
	fr.read(carr); 
	
    // 배열 출력
	for(int i = 0; i < carr.length; i++) {
		System.out.println(carr[i]);
	} 
			
} catch (FileNotFoundException e) {
	e.printStackTrace();
} catch (IOException e) { // read()의 예외 처리 중
	e.printStackTrace();
} finally {
	if(fr != null) {
		try {
			fr.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

◼ testReader.txt File

안녕하세요

💻 Mini Console

안
녕
하
세
요

4) FileWriter

프로그램의 데이터를 외부 데이터인 File로 한 문자씩 내보내는 출력 스트림

◼ Application Class

FileWriter fw = null;
		
try {
	>>> 출력 스트림의 경우 대상 파일이 존재하지 않으면 파일을 자동으로 생성하나 경로가 존재하지않는 경우는 예외 발생
	>>> 두 번째 인자로 true 전달 시, 이어쓰기
	fw = new FileWriter("src/com/greedy/section02/stream/testWriter.txt"/*, true*/);
			
	fw.write(97); 
    >>> testWriter.txt에 a가 내보내기되지 않음
			
	>>> 문자 단위 출력도 내부 버퍼를 사용하므로 쌓여있는 데이터를 flush()로 내보내줘야 
    >>> 최종적으로 파일에 출력.  
	>>> 또는 close()로 자원을 반납하면 반납 전에 flush() 메소드가 내부적으로 호출되므로 파일에 출력
	
    fw.flush();
	>>> testWriter.txt에 a가 내보내짐
			
	// 문자 기반 스트림은 직접 char 자료형으로 내보내기 가능
	fw.write('A');
			
	// 또는 char 배열도 가능
	fw.write(new char[] {'a', 'b', 'c', 'd', 'e'});
			
	// 또는 String도 가능
	fw.write("월요일 싫어 치즈 좋아");
			
} catch (IOException e) {
	e.printStackTrace();
} finally {
	if(fw != null) {
		try {
			fw.close(); 
            >>> 닫기 전에 버퍼에 쌓여있는 데이터를 내보내기함 
            >>> flush를 호출하지 않더라도 닫히면서 쌓여있던 값이 내보내기됨 => a 출력
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

◼ testWriter.txt File

aAabcde월요일 싫어 치즈 좋아

👀 네 가지의 Filter Stream

💁‍♀️ 필터 스트림이란,
필터 스트림(보조 스트림)은 외부 데이터에 직접 연결하는 것이 아니라 기본 스트림에 추가로 사용할 수 있는 스트림

  • 주로 성능을 향상시키는 목적으로 사용하며 생성자를 보면 구분이 가능
  • 생성자 쪽에 매개변수로 다른 스트림을 이용하는 클래스는 필터 스트림이라고 볼 수 있음

📍 Filter Stream의 종류

1) 성능 향상 보조 스트림
2) 형변환 보조 스트림
3) 기본 데이터 타입 보조 스트림
4) 객체 입출력 보조 스트림

1) 성능 향상 보조 스트림

BufferedInputStream BufferedOutputStream
BufferedReader BufferedWriter

◼ Application Class

✅ BufferedWriter
BufferedWriter bw = null;
		
try {
	bw = new BufferedWriter(new FileWriter("src/com/greedy/section03/filterstream/testBuffered.txt")); 
	// 보조 : bw = new BufferedWriter / 기반 : new FileWriter("src/com/greedy/section03/filterstream/testBuffered.txt")
    >>> 서로 데이터 타입 맞춰줘야함 (Writer)
			
	bw.write("오늘의 점심은?\n");
	bw.write("칼국수\n");
			
	>>> 버퍼를 이용하는 경우 버퍼가 가득 차면 자동으로 내보내기를 하지만 버퍼가 차지않은 상태에서는 
    >>> flush()를 호출하여 강제 내보내기를 해야함 (flush()를 해주면 파일에 기록됨)
	bw.flush();
			
} catch (IOException e) {
	e.printStackTrace();
} finally {
	if(bw != null) {
		try {
			>>> 사용한 보조 스트림만 close()하면 내부적으로 기반 스트림도 close()됨
			bw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
✅ BufferedReader
>>> 버퍼에 미리 읽어온 뒤 한 줄 단위로 읽어들이는 기능을 제공하며,
>>> 기본 스트림보다 성능을 개선시킨 보조 스트림
		
BufferedReader br = null;
		
try {
	br = new BufferedReader(new FileReader("src/com/greedy/section03/filterstream/testBuffered.txt"));
			
	>>> BufferedReaderreadLine() 메소드를 추가로 제공
    >>> 버퍼의 한 줄을 읽어와서 문자열로 반환
	String temp;
	while((temp = br.readLine()) != null) { 
  
		System.out.println(temp); // 버퍼를 한 줄씩 읽어와서 콘솔창에 출력
	}	
			
} catch (FileNotFoundException e) {
			
	e.printStackTrace();
} catch (IOException e) { // readLine()의 예외 처리 중
	e.printStackTrace();
} finally {
	if(br != null) {
		try {
			br.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

◼ testBuffered.txt File

오늘의 점심은?
칼국수

💻 Mini Console

오늘의 점심은?
칼국수

📌 Ref.

* readLine()IOException을 핸들링 해야함
* readLine()은 스트링 값을 읽어오므로 반환할 값이 없을 때, -1가 아닌 null을 반환

2) 형변환 보조 스트림

InputStreamReader OutputStreamWriter

기본 스트림이 byte 기반 스트림이고, 보조 스트림이 char 기반 스트림인 경우 사용

📌 Ref. 표준 스트림

* 콘솔이나 키보드 같은 표준 입력 장치로부터 데이터를 입출력하기위한 스트림을 '표준 스트림 형태'로 제공
* System 클래스의 필드 in, out, err가 대상 데이터의 스트림을 의미
* System.in (InputStream) : 콘솔로부터 데이터를 입력 받기
* System.out (PrintStream) : 콘솔로 데이터를 출력하기
* System.err (PrintStream) : 콘솔로 데이터를 출력하기 (빨간색)
* 자주 사용되는 자원에 대해 미리 스트림을 생성해두었기 때문에 개발자가 별도로 스트림을 생성하지 않아도 됨

◼ Application Class

✅ InputStreamReader
>>> Buffer를 이용해서 성능을 향상시키고 싶은 경우, '형변환 보조 스트림'을 사용

//	BufferedReader br = new BufferedReader(System.in); 
	>>> inputStream(System.in)을 직접적인 인자로 넣을 수 없음 (지원X)
    
	BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 
		
	try {
		System.out.print("문자열 입력 : ");
		String value = br.readLine(); 
        >>> 개행(Enter)전까지의 한 줄을 읽어오기
        >>> 스캐너와 동일하게 사용되지만 데이터 타입을 입력하지 않고 readLine()만으로 출력 가능
		System.out.println("value : " + value);
			
	} catch (IOException e) {
		e.printStackTrace();
	} finally {
		if(br != null) {
			try {
				br.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
✅ OutputStreamWriter
>>> 출력을 위한 것도 동일한 방식
>>> try-with-resource 문으로 작성

//	BufferedWriter bw = 버퍼 보조 스트림(형변환 보조 스트림(표준 출력 스트림_기반 스트림));
//	BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
>>> try 옆에 소괄호를 만든 후, 이 식을 그대로 넣어주면 try-with-resource 문 
	=> finallyclose() 구문을 작성하지 않아도 close됨
		
	try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));){
		bw.write("Joy with Happy"); >>> write() : 콘솔로 내보내는 역할
	} catch (IOException e) {
		e.printStackTrace();
	}

💻 Mini Console

문자열 입력 : 내일은 화요일 :)
value : 내일은 화요일 :)
Joy with Happy

3) 기본 데이터 타입 보조 스트림

DataInputStream DataOutputStream

외부 데이터로부터 읽어오는 데이터를 Byte형으로만 읽어오면 정수, 문자, 문자열 등 여러 데이터 타입을 취급하는 경우 별도의 변환 처리를 해야하기 때문에 번거로움. 따라서, 데이터 자료형 별로 처리하는 기능을 추가한 기본 데이터 타입 보조 스트림을 사용

◼ Application Class

✅ DataOutputStream
// 데이터형별로 파일에 기록하는 DataOutputStream 인스턴스 생성
try (DataOutputStream dout 
	= new DataOutputStream(new FileOutputStream("src/com/greedy/section03/filterstream/score.txt"))) {
			
	// 파일에 자료형별로 기록
	dout.writeUTF("조효연"); >>> String은 writeStirng이 아닌 writeUTF
	dout.writeInt(100); >>> Int는 눈에 보이지 않음 (다른 것으로 출력됨)
	dout.writeChar('A');
			
	dout.writeUTF("허수민"); 
	dout.writeInt(85);
	dout.writeChar('B');
			
	dout.writeUTF("허치즈"); 
	dout.writeInt(79);
	dout.writeChar('C');
			
} catch (FileNotFoundException e) {
	e.printStackTrace();
} catch (IOException e) {
	e.printStackTrace();
} >>> try-with-resource로 인해 Finallyclose()구문을 작성하지않아도 자동 close
✅ DataInputStream
// 데이터형 별로 읽어오는 DataInputStream 인스턴스 생성
try (DataInputStream din 
		= new DataInputStream(new FileInputStream("src/com/greedy/section03/filterstream/score.txt"))) {
			
	>>> 무한 루프로 읽어들이게 되면 파일에 더 이상 읽을 것이 없을 때, EOFException이 발생한다.
	>>> EOF(End Of File)Exception : 더이상 읽어올 것이 없음
	>>> catch 블럭에 EOFException을 핸들링하는 코드를 추가하면됨
	while(true) {
		>>> 파일에 기록한 순서대로 읽어들이지 않는 경우 에러가 발생하거나 올바르지 않은 값을 읽어옴
		System.out.println(din.readUTF());
		System.out.println(din.readInt());
		System.out.println(din.readChar()); 
        >>> 이 식이 readInt() 위로 올라가면 출력문이 비정상적
	}

} catch (FileNotFoundException e) {
	e.printStackTrace();
} catch (EOFException e) {	>>> EOFExceptionIOException보다 하위 타입이므로 IOException보다 위에 있어야함 
							>>> (아래에 있으면 unreachable 코드)
	System.out.println("파일 읽기를 완료했습니다.");
} catch (IOException e) {
	e.printStackTrace();
}

◼ score.txt File

     조효연   d A  허수민    U B  허치즈   O C

💻 Mini Console

조효연
100
A
허수민
85
B
허치즈
79
C
파일 읽기를 완료했습니다.

4) 객체 입출력 보조 스트림

ObjectInputStream ObjectOutputStream

◼ MemberDTO Class

>>> 직렬화 대상 클래스에 Serializable 인터페이스만 구현하면 직렬화가 필요한 경우 데이터 직렬화 처리가 수행됨
public class MemberDTO implements Serializable { 

>>> MemberDTO에 커서를 가져다 대어 'Add generated serial version ID'를 누르면 
>>> 시리얼 버전 아이디 생성 => UID가 fix되어 변경 사항이 발생하더라도 역직렬화가 가능

	>>> static : 모든 객체가 공유하는 공간
	private static final long serialVersionUID = -1852199523260481785L; 
	private String id;
	private String pwd;
	private int age;
	private char gender;
	>>> 직렬화 대상에서 제외하고 싶은 필드의 경우 transient 키워드를 이용할 수 있음
	private transient double point; // 0.0 출력
	
	public MemberDTO() {}

	public MemberDTO(String id, String pwd, int age, char gender, double point) {
		super();
		this.id = id;
		this.pwd = pwd;
		this.age = age;
		this.gender = gender;
		this.point = point;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getPwd() {
		return pwd;
	}

	public void setPwd(String pwd) {
		this.pwd = pwd;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public char getGender() {
		return gender;
	}

	public void setGender(char gender) {
		this.gender = gender;
	}

	public double getPoint() {
		return point;
	}

	public void setPoint(double point) {
		this.point = point;
	}

	@Override
	public String toString() {
		return "MemberDTO [id = " + id + ", pwd = " + pwd + ", age = " + age + ", gender = " + gender + ", point = " + point
				+ "]";
	}
}

📌 Ref.

* SerialVersionUID를 작성하지 않았을 경우, JVM이 자동으로 SerialVersionUID를 생성해서 
  동작시키므로 오류가 발생하지 않았음. 다만 '추후에 Class에 변경 사항이 발생하면' 다시 
  자동으로 또 다른 SerialVersionUID를 생성하게 되므로 이전에 출력했던 
  SerialVersionUID와 불일치하여, 읽어올 때 역직렬화에 실패하는 상황(InvaildCalssException)이 
  생길 수 있음. 따라서 '명시적으로 작성'해두는 것이 좋음

◼ Application Class

✅ ObjectOutputStream
MemberDTO[] outputMembers = {
	new MemberDTO("user01", "pass01", 26, '여', 1250.7),	
	new MemberDTO("user02", "pass02", 30, '남', 1212.9),
	new MemberDTO("user03", "pass03", 35, '여', 1243.6)	
};
		
try (ObjectOutputStream oos 
	= new ObjectOutputStream(new FileOutputStream("src/com/greedy/section03/filterstream/testObjectStream.txt"))){
			
	for(int i = 0; i < outputMembers.length; i++) {
    	// 직렬화 중
		oos.writeObject(outputMembers[i]);  
        >>> writeObject : 객체를 출력하는 메소드 (파일 쪽으로 내보내기)
	}	>>> 직렬화 후, 실행하면 예외가 발생하지않고 왼쪽의 filterstream을 
    	>>> Refresh하면 testObjectStream.txt 파일이 생성되어있음
			
} catch (FileNotFoundException e) {
	e.printStackTrace();
} catch (IOException e) {
	e.printStackTrace();
}

📌 Ref.

* 처음에 실행하면 java.io.NotSerializableException 발생. 
  이는 '직렬화 처리를 해주지 않아서 발생'하는 예외

🙋‍ 잠깐 ! 직렬화와 역직렬화가 뭔가요?

  • 직렬화
    : 자바 시스템 내부에서 사용되는 객체 또는 데이터를 외부에서도 사용할 수 있도록 byte 형태로 데이터를 변환하는 기술
  • 역직렬화
    : 바이트로 변환 된 데이터를 다시 객체로 변환하는 기술
✅ ObjectInputStream
MemberDTO[] inputMembers = new MemberDTO[3];
>>> outputMembers에서 내보낸 파일을 inputMembers에 담아 읽어옴
		
try (ObjectInputStream ois = new ObjectInputStream(
		new FileInputStream("src/com/greedy/section03/filterstream/testObjectStream.txt"))) {
			
	for(int i = 0; i < inputMembers.length; i++) {
		// 역직렬화 중
        inputMembers[i] = (MemberDTO) ois.readObject(); 
        >>> readObject()를 통해 객체를 읽어서 inputMembers[i]에 저장할 것 
        >>> *MemberDTO로 다운캐스팅 
        >>> readObject()ClassNotFoundException 발생. 핸들링 해야함
	}
			
} catch (FileNotFoundException e) {
	e.printStackTrace();
} catch (IOException e) {
	e.printStackTrace();
>>> readObject()로 읽어온 Object를 역직렬화 할 수 있는 Class가 없을 때 ClassNotFoundException이 발생하기 때문에 핸들링 해야함
} catch (ClassNotFoundException e) { 
	e.printStackTrace();
}
		
// 파일에서 읽어온 객체 결과 출력
for(MemberDTO member : inputMembers) {
	System.out.println(member);
}

📌 Ref.

* MemberDTO로 다운캐스팅 하는 이유 ?  
  inputMembers[i]MemberDTO 클래스 타입인 반면, readObject()Object 클래스 타입이기 때문에 
  강제 타입 형변환을 해야함

◼ testObjectStream.txt File

��

profile
Tiny little habits make me

0개의 댓글

관련 채용 정보