입출력 스트림
- 프로그램을 기준으로 데이터가 들어오면 입력 스트림, 데이터가 나가면 출력 스트림
- 프로그램이 다른 프로그램과 데이터를 교환하기 위해서는 양쪽 프로그램 모두 입력 스트림과 출력 스트림이 필요하다!
IPC 프로그램

- 자바는 데이터 입출력과 관련된 라이브러리를 java.io 패키지에서 제공함

스트림의 종류
- 바이트 스트림 : 그림, 멀티미디어, 문자 등 모든 종류의 데이터를 입출력할 때 사용
-> 바이트 스트림의 최상위 클래스 : InputStream(입력), OutputStream(출력)
- 문자 스트림 : 문자만 단독으로 입출력할 때 사용함
-> 문자 스트림의 최상위 클래스 : Reader, Writer
- 스트림은 위의 기능들을 기본으로 함!

바이트 스트림
바이트 출력 스트림(OutputStream)
- OutputStream은 바이트 출력 스트림의 최상위 클래스로 추상 클래스임
-> 객체로 생성 불가능함
- 모든 바이트 출력 스트림 클래스는 해당 클래스를 상속받아서 생성됨

- OutputStream 클래스에는 모든 바이트 출력 스트림이 기본적으로 가져야 할 메소드가 정의됨

- Visual Studio Code 프로그램 실행 및 해당 데이터베이스 파일 열기
- view > extensions > hex editor 설치
- F1키 누르고 Hex Editor Open Active File in Hex Editor 설치 (16진수로 볼 수 있음)
1바이트 출력
- write(int b) 메소드 : 매개값 int에서 끝 1byte만 출력함. 매개변수는 int 타입!

바이트 배열 출력
- write(byte[] b) 메소드 : 매개값으로 주어진 배열의 모든 바이트를 출력함
- 배열의 일부분을 출력하려면 write(byte[] b, int off, int len) 메소드를 사용함

- InputStream은 바이트 입력 스트림의 최상위 클래스로, 추상 클래스임
- 모든 바이트 입력 스트림은 InputStream 클래스를 상속받아 만들어짐

- InputStream 클래스에는 바이트 입력 스트림이 기본적으로 가져야 할 메소드가 정의됨

1바이트 입력
- read() 메소드 : 입력 스트림으로부터 1byte를 읽고 int 타입으로 리턴함.
- 리턴된 4byte중 끝 1byte에만 데이터가 들어 있음

- 더 이상 입력 스트림으로부터 바이트를 읽을 수 없다면 read() 메소드는 -1을 리턴함
- 읽을 수 있는 마지막 바이트까지 반복해서 한 바이트씩 읽을 수 있음
바이트 배열로 읽기
- read(byte[]b) 메소드 : 입력 스트림으로부터 주어진 배열의 길이만큼 바이트를 읽고 배열에 저장한 다음 읽은 바이트 수를 리턴
- read(byte[]b)도 입력 스트림으로부터 바이트를 더 이상 읽을 수 없다면 -1을 리턴
- 읽을 수 있는 마지막 바이트까지 반복해서 읽을 수 있음
문자 입출력 스트림
문자 출력 (Writer)
- Writer : 문자 출력 스트림의 최상위 클래스
- 모든 문자 출력 스트림 클래스는 Writer 클래스를 상속받아 생성됨
- 문자로만 출력받으며 Ascii 코드 사용 불가능
- 스트림은 순차적으로 읽어들이고 내보내는 작업 수행!

- Writer 클래스에는 모든 문자 출력 스트림이 기본적으로 가져야 할 메소드가 정의됨

문자 입력 (Reader)
- Reader는 문자 입력 스트림의 최상위 클래스로, 추상 클래스에 해당됨
- 모든 문자 입력 스트림 클래스는 Reader 클래스를 상속받아서 생성됨

- Reader 클래스에는 문자 입력 스트림이 기본적으로 가져야 할 메소드가 정의된다

보조 스트림
- 다른(바이트, 문자) 스트림과 연결되어 여러 편리한 기능을 제공해주는 스트림
- 자체적으로 입출력을 수행할 수 없음
- 입출력 소스로부터 직접 생성된 입출력 스트림에 연결해서 사용함

- 입출력 스트림에 보조 스트림을 연결하려면 보조 스트림을 생성할 때 생성자 매개값으로 입출력 스트림을 제공
- 보조스트림 변수 = new 보조스트림(입출력스트림(기본 스트림));
- 보조스트림은 또 다른 보조스트림과 연결되어 스트림 체인으로 구성할 수 있음

- 보조스트림2 변수 = new 보조스트림2(보조스트림1);

문자 변환 스트림 (많이 사용함)
- InputStream을 Reader로 변환하려면 InputStreamReader 보조 스트림을 연결
- 스트림을 가지고는 줄 단위로 읽고 쓰기가 불편하기 때문에 변환을 시행함!
- Reader, Writer는 줄 단위로 읽고 쓰는 기능이 있음!

OutputStream을 Writer로 변환
- OutputStream을 Writer로 변환하려면 OutputStreamWriter 보조 스트림을 연결
- 스트림을 가지고는 줄 단위로 읽고 쓰기가 불편하기 때문에 변환을 시행함!
- Reader, Writer는 줄 단위로 읽고 쓰는 기능이 있음!

성능 향상 스트림
메모리 버퍼로 실행 성능을 높이는 보조 스트림
- 프로그램이 중간에 메모리버퍼와 작업해서 실행 성능 향상이 가능함
- 출력 스트림의 경우 직접 하드 디스크에 데이터를 보내지 않고 메모리 버퍼에 데이터를 보냄으로써 출력 속도를 향상
- 입력 스트림에서도 버퍼를 사용하면 읽기 성능이 향상됨

- 바이트 스트림 : BufferedInputStream, BufferedOutputStream
- 문자 스트림 : BufferedReader, BufferedWriter
기본 타입 스트림
- 바이트 스트림에 DataInputStream과 DataOutputStream 보조 스트림을 연결하면 기본 타입 값을 입출력할 수 있음

프린트 스트림
- 프린터와 유사하게 출력하는 print(), println(), printf() 메소드를 가진 보조 스트림

- PrintStream : 바이트 출력 스트림과 연결됨
- PrintWriter : 문자 출력 스트림과 연결됨

직렬화와 역직렬화 (개념 중요!)
- 리플렉션을 활용하여 객체를 저장했다가 읽어들이는데 사용
직렬화
- 메모리에 생성된 객체를 파일 또는 네트워크로 출력하기 위해 필드값을 일렬로 늘어선 바이트로 변경하는 것!
- 객체를 파일로 저장하는 역할!
- ObjectOutputStream : 바이트 출력 스트림과 연결되어 객체를 직렬화 함
역직렬화
- 직렬화된 바이트를 객체의 필드값으로 복원하는 것
- 파일에 내용을 읽어들이면 객체로 만드는 것
- ObjectInputStream : 바이트 입력 스트림과 연결되어 객체를 복원하는 역직렬화

Serializable 인터페이스
- 멤버가 없는 빈 인터페이스
- 객체를 직렬화할 수 있다고 표시하는 역할을 함
- 인스턴스 필드값은 직렬화 대상임
- 정적 필드값과 transient로 선언된 필드값은 직렬화에서 제외되므로 출력되지 않는다
serialVersionUID 필드
- 직렬화할 때 사용된 클래스와 역직렬화할 때 사용된 클래스는 동일한 클래스여야 함
- 클래스 내용이 다르더라도
File(많이 사용)
- File 클래스로부터 File 객체를 생성함
- exists() 메소드로 파일 또는 폴더의 존재여부를 확인함
- exists() 메소드가 false를 리턴할 경우 다음 메소드로 파일 또는 폴더를 생성함

- exists() 메소드의 리턴값이 true라면 다음 메소드를 사용함

package ch18.sec11;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
public class FileExample {
public static void main(String[] args) throws Exception{
// 폴더 생성
File dir = new File("C:/Temp/images");
// 파일 생성!
File file1 = new File("C:/Temp/file1.txt");
File file2 = new File("C:/Temp/file2.txt");
File file3 = new File("C:/Temp/file3.txt");
// 폴더가 존재하지 않을 때!
if(!dir.exists()) {
// 폴더 생성
// mkdir : 해당 폴더(디렉토리)만 생성함, 상위 디렉토리가 존재하지 않으면 생성 불가능
// mkdirs: 해당 폴더(디렉토리)와 상위 디렉토리까지 모두 생성 (상위 디렉토리가 없을 경우)
dir.mkdirs();
}
// 파일이 존재하지 않을 때!
if(!file1.exists()) {
// 파일 생성
file1.createNewFile();
}
if(!file2.exists()) {
file2.createNewFile();
}
if(!file3.exists()) {
file3.createNewFile();
}
// C:/Temp를 지정
File temp = new File("C:/Temp");
// .listFiles() : 디렉토리의 모든 파일과 하위 디렉토리를 배열로 반환하는 작업
File[] contents = temp.listFiles();
// 날짜의 형식에 맞춤!
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd a HH:mm");
// File 배열에 저장된 값들을 차례대로 출력
for(File file : contents) {
// 25칸의 문자열 공간을 확보하고, 최종 수정시간을 밀리초로 가져옴(file.laseModified())
System.out.printf("%-25s", sdf.format(new Date(file.lastModified())));
// 해당 파일이 디렉토리(폴더)일때
if(file.isDirectory()) {
System.out.printf("%-10s%-20s", "<DIR>", file.getName());
}
// 해당 파일이 디렉토리가 아닐 때!
else {
System.out.printf("%-10s%-20s", file.length(), file.getName());
}
System.out.println();
}
}
}
Files 클래스
- Files 클래스는 정적 메소드로 구성되어 있음. File 클래스처럼 객체로 만들 필요가 없음
- Files의 정적 메소드는 운영체제의 파일 시스템에게 파일 작업을 수행하도록 위임

- 상대 경로 : "."(하위폴더), ".."(상위폴더)
- 절대 경로 : "/", "//"
- cmd에서 파일 생성 확인 방법 : type 파일명.파일확장자명
- cmd에서 파일 복사 방법 : copy 파일명1.파일확장자명(복사할 파일) 파일명2.파일확장자명(복사본 파일 이름)
- cmd에서 파일 삭제 방법 : del 파일명.파일확장자명
package ch18.sec11;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class FilesExam {
public static void main(String[] args) {
try {
String data = "" +
"id:dongju\n" +
"email: dongju@mycompany.com\n" +
"tel: 010-1234-5678";
// Path 클래스 : 파일이나 디렉토리의 경로를 나타냄
// Paths 클래스 : Path 객체를 생성하기 위해 사용하는 유틸리티 클래스
// get() 메소드를 통해 경로 문자열을 받고 Path 객체를 생성
Path path = Paths.get("C:/Temp/user.txt");
// writeString() : Files 클래스에 있는 정적 메소드, 파일에 문자열을 쉽게 작성 가능
// writeString(a,b,c)
// a : Path 객체를 생성(Paths)하여 경로를 문자열로 표시 및 생성
// b : 파일에 쓸 문자열 데이터
// c : charset.forName("UTF-8") -> 문자 인코딩 방식을 지정함 (생략시 utf-8이 디폴트)
Files.writeString(Paths.get("C:/Temp/user.txt"), data, Charset.forName("UTF-8"));
// Files.probeContentType(파일경로) : 해당 파일의 확장자나 파일 내용을 나타냄
System.out.println("파일 유형: " + Files.probeContentType(path));
// Files.size(파일경로)
// -> 해당 파일의 크기를 출력 (바이트로 출력)
System.out.println("파일 크기: " + Files.size(path) + " bytes");
// Files.readString(파일경로, 인코딩방식)
// -> 해당 인코딩 방식을 이용하여 파일을 문자열로 읽어옴
String content = Files.readString(path, Charset.forName("UTF-8"));
System.out.println(content);
}
catch(Exception e) {
e.printStackTrace();
}
}
}