▶ 입출력 (IO)이란?
- 컴퓨터 내부 또는 외부 장치와 프로그램 간의 데이터를 주고 받는 것
- 다양한 매체에 존재하는 데이터들을 사용하기 위해 입출력 데이터를 처리할 공통적인 방법으로 스트림(Stream) 이용
- Input(입력) : 외부 → 내부로 데이터를 들여오는 것
- Output(출력) : 내부 → 외부로 데이터를 내보내는 것
- Stream(스트림) : 데이터가 이동하는 통로 (기본적으로 단 방향)
▶ File 클래스
- 파일 시스템의 파일을 표현하는 클래스
- 파일 크기, 속성, 파일 이름 등의 정보와 파일 생성 및 삭제 기능 제공
File file = new File("C:/io/test.txt")
▶ 스트림 (Stream) 클래스
- 입출력 장치에서 데이터를 읽고 쓰기 위해서 자바에서 제공하는 클래스
- 모든 스트림은 단방향
- 하나의 스트림으로 입출력을 동시에 수행할 수 없으므로, 동시에 수행하려면 2개의 스트림 필요
▶ InputStream
- 바이트 기반 입력 스트림의 최상위 클래스로 추상 클래스
- 1byte 단위로 데이터를 입력하는 스트림
- 1byte 범위 문자열 (아스키 코드 (영어, 숫자, 특수문자)) 또는 이미지, 영상, 오디오 등 문자가 아닌 파일 / 데이터
▶ FileInputStream
- 파일을 바이트 단위로 읽을 때 사용
- 그림, 오디오, 비디오, 텍스트 파일 등 모든 종류의 파일 읽기 가능
- InputStream의 하위 클래스
public void byteInput() { // 바이트 기반 파일 입력
// FileInputStream 객체 생성 시
// FileNOtFoundException 예외가 발생할 가능성 있음 → 예외 처리 필요
FileInputStream fis = null;
try {
fis = new FileInputStream("lyrics/APT.txt");
// * 읽어올 땐 한 글자씩 *
int value = 0; // 읽어온 한 글자를 저장할 변수 선언
while(true) {
value = fis.read(); // 다음 1byte를 읽어와 int로 저장 / 다음 내용이 없으면 -1 반환
if(value == -1) break; // 다 읽었으면 while문 탈출
System.out.print((char)value);
}
} catch(IOException e) { // IO 관련 코드는 IOException을 발생시킬 가능성이 높음
e.printStackTrace(); // 예외가 발생한 메소드까지의 모든 내용 출력
} finally { // 예외 발생여부 상관 없이 무조건 실행
try {
// 사용한 Stream 자원 반환(통로 없앰/메모리 반환)
if(fis != null) fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
▶ OutputStream
- 바이트 기반 출력 스트림의 최상위 클래스로 추상 클래스
- 1byte 단위로 데이터를 출력하는 스트림
- 1byte 범위 문자열 (아스키코드(영어, 숫자, 특수문자)) 또는 이미지, 영상, 오디오 등 문자가 아닌 파일 / 데이터
▶ FileOutputStream
- 파일을 바이트 단위로 저장할 때 사용
- 그림, 오디오, 비디오, 텍스트 파일 등 모든 종류의 데이터를 파일로 저장
- OutputStream의 하위 클래스
public void byteOutput() {
// FileOutputStream 객체 생성 시
// FileNotFoudException 예외가 발생할 가능성이 있음 -> 예외 처리 필요
FileOutputStream fos = null;
try {
// byte 기반 파일 출력 Stream 생성
fos = new FileOutputStream("byte/byteTest.txt");
// byte 폴더 내부에 byteTest.txt 파일이 있으면 출력을 위해 연결하고,
// 없으면 파일을 만들어서 연결
String content = "Hello World\n"
+ "IO 예제 작성중\n"
+ 1234567
+ "\n~!@#$%";
for(int i=0; i<content.length(); i++) { // 글자 수만큼 반복
// 한 문자씩 끊어서 파일로 출력하기
fos.write(content.charAt(i)); // i번째 문자 하나
}
System.out.println("출력 완료");
} catch(IOException e) { // IO 관련 코드는 IOException을 발생시킬 가능성이 높음
System.out.println("예외 발생");
e.printStackTrace(); // 예외가 발생한 메소드까지의 모든 내용 출력
} finally { // 예외 발생여부 상관 없이 무조건 실행
// 사용한 Stream 자원 반환 (통로 없앰) --> 필수 작성!!
// 프로그램 메모리 관리 차원에서 항상 다 쓰면 끊어줌
try {
if(fos != null) fos.close(); // 작성된 코드가 한 줄일 경우, 중괄호 생략 가능
} catch (IOException e) {
e.printStackTrace();
}
}
}
▶ Reader
- 문자 기반 입력 스트림의 최상위 클래스로 추상클래스
- 2byte 단위로 데이터(문자)를 입력하는 스트림
- 2byte 범위의 문자열, 문자만 저장된 파일, 채팅, 인터넷 요청 시 데이터 전달 등에 이용
▶ FileReader
- 문자 단위로 텍스트 기반 파일을 읽을 때 사용
- 텍스트가 아닌 그림, 오디오, 비디오 등의 파일은 읽기 불가능
- Reader의 하위 클래스
public void charInput() {
FileReader fr = null;
try {
fr = new FileReader("lyrics/APT.txt");
// * 읽어올 땐 한 글자씩 *
int value = 0; // 읽어온 한 글자를 저장할 변수 선언
while(true) {
value = fr.read(); // 다음 1byte를 읽어와 int로 저장 / 다음 내용이 없으면 -1 반환
if(value == -1) break; // 다 읽었으면 while문 종료
System.out.print((char)value);
}
} catch(IOException e) {
e.printStackTrace();
} finally {
try {
if(fr != null) fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
▶ Writer
- 문자 기반 출력 스트림의 최상위 클래스로 추상 클래스
- 2byte 단위로 데이터(문자)를 출력하는 스트림
- 2byte 범위의 문자열, 문자만 저장된 파일, 채팅, 인터넷 요청 시 데이터 전달 등에 이용
▶ FileWriter
- 문자 단위로 텍스트 기반 파일을 쓸(저장할) 때 사용
- 텍스트가 아닌 그림, 오디오, 비디오 등의 파일은 저장 불가능
- Writer의 하위 클래스
public void charOutput() {
// 프로그램 -> 파일에 씀
FileWriter fw = null;
try {
// fw = new FileWriter("경로", 이어쓰기 옵션_기본값:false);
// -> byte 기반 Stream도 사용 가능한 옵션
fw = new FileWriter("char/charTest.txt", true);
// char 폴더에 charTest.txt가 있으면 문자 출력 Stream 연결
// 없으면 만들어서 연결
String content = "\n오늘은 회식날 입니다.\n"
+ 1234567
+ "\n~!@#$%^\n"
+ "abcdefgh";
fw.write(content); // 문자열을 통째로 내보냄
// -> 한 줄을 통째로 내보내기 위해 "버퍼"를 이용하는데
// 아직 버퍼에 담겨 있음!
// --> 이걸 강제로 밀어넣어서 버퍼를 비워야 함!
System.out.println("char 기반 출력 완료");
} catch(IOException e) {
e.printStackTrace();
} finally {
try {
// fw Stream 객체가 있을 때에만 닫기(메모리 반환)
// close() 구문을 수행하면 통로에 남아있는 내용을 모두 내보내고
// 통로를 없앤다!
if(fw != null) fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
▶ 보조 스트림 종류
- 문자 변환 : InputStreamReader / OutputStreamWriter
- 입출력 성능 : BufferedInputStream / BufferedOutputStream
- 기본 데이터 타입 출력 : DataInputStream / DataOutputStream
- 객체 입출력 : ObjectInputStream / ObjectOutputStream
▶ 성능 향상 보조 스트림 (BufferedInputStream / BufferedOutputStream)
- 느린 속도로 인해 입출력 성능에 영향을 미치는 입출력 소스를 이용하는 경우 사용
- 입출력 소스와 직접 작업하지 않고, 버퍼에 데이터를 모아 한꺼번에 작업을 하여 실행 성능 향상(입출력 횟수 줄임)
public void fileCopy() {
Scanner sc = new Scanner(System.in);
// byte 기반 Stream 이용
// + 성능 향상을 위한 보조 Stream 추가
// -> BufferedInputStream / BufferedOuputStream
// -> 모아서 한 번에 입/출력
BufferedInputStream bis = null; // 보조 Stream(Input 용)
BufferedOutputStream bos = null; // 보조 Stream(Output 용)
try {
System.out.println("복사할 파일 경로 : ");
String target = sc.nextLine(); // 한 줄 입력
System.out.println("복사본이 저장될 경로 + 파일명 : ");
String copy = sc.nextLine();
// 복사 대상을 읽어올 InputStream 생성 + 보조 Stream으로 성능 향상
bis = new BufferedInputStream( new FileInputStream(target) );
// 보조 Stream( 기반 Stream ) : 보조 Stream 내부에 기반 Stream 넣기
// 복사한 파일을 출력할 OutputStream 생성 + 보조 Stream으로 성능 향상
bos = new BufferedOutputStream( new FileOutputStream(copy) );
int value = 0; // 1byte씩 읽어서 저장할 임시 변수
while(true) {
value = bis.read(); // 1byte씩 읽어오기, 없으면 -1 반환
if(value == -1) break;
bos.write(value); // 1byte씩 출력하기
}
System.out.println("복사 완료");
} catch (FileNotFoundException e) { // 파일 경로 잘못 입력한 경우 예외 처리
System.out.println("해당 파일이 존재하지 않습니다.");
} catch(IOException e){ // IO 관련 예외 발생할 경우 예외 처리(IO예외 상위 클래스)
e.printStackTrace();
} finally {
try {
if(bis != null) bis.close(); // 보조 Stream을 close()하면
if(bos != null) bos.close(); // 연결된 기반 Stream도 같이 close() 된다.
} catch (IOException e) {
e.printStackTrace();
}
}
}
▶ 객체 입출력 보조 스트림 (ObjectInputStream / ObjectOutputStream)
- 객체를 파일 또는 네트워크로 입출력할 수 있는 기능 제공
- 단, 객체는 문자가 아니므로 바이트 기반 스트림으로 데이터를 변경해주는 직렬화 필수
▶ 직렬화와 역직렬화
1) 직렬 (Serialization)
- Serializable 인터페이스를 implements 하여 구현
- Serializable 인터페이스 : 해당 클래스가 객체로 만들어지면, 직렬화가 가능함을 나타냄을 표시 (마커 인터페이스)
2) 역직렬화 (Deserialization)
- 직렬화된 객체를 역직렬화할 때는 직렬화했을 때와 같은 클래스 사용
- 단, 클래스 이름이 같더라도 클래스 내용이 변경된 경우, 역직렬화 실패
3) serialVersionUID 필드
- 직렬화한 클래스와 같은 클래스임을 알려주는 식별자 역할
- 컴파일 시 JVM이 자동으로 추가
- 단, 자동 생성 시 역직렬화에서 예상치 못한 InvalidClassException 유발할 수 있어 명시 권장
private static final long serialVersionUID = -123456789123456L;
public void objectOutput() { // 객체 출력 보조스트림
// ObjectOutputStream : 객체를 바이트 기반으로 출력하는 보조 스트림
ObjectOutputStream oos = null;
try {
// java.io.File : 파일/폴더를 참조하는 객체
File folder = new File("object"); // object라는 이름의 파일/폴더를 참조
if(!folder.exists()) { // object 폴더가 존재하지 않는다면
folder.mkdir(); // make directory(==folder) : 폴더 만들기
}
oos = new ObjectOutputStream( new FileOutputStream("object/Member.txt") );
// 기반 스트림 : 생성된 스트림 객체를 이용해 직접 입출력을 수행할 수 있는 스트림
// 보조 스트림 : 스트림 객체로 생성되어도 직접 입출력 할 수 없고,
// 기반 스트림의 성능을 향상 시키거나 새로운 기능을 추가하기 위해 사용
// 내보낼 회원 객체 생성
Member mem = new Member("member01","pass01","일번멤버",1000);
// 회원 객체를 파일로 출력
oos.writeObject(mem);
System.out.println("회원 출력 완료");
// NotSerialization 발생 -> Member 클래스 Serializable 인터페이스 implements 필요
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(oos != null) oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void objectInput() { // 객체 입력 보조스트림
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream( new FileInputStream("object/Member.txt") );
Member mem = (Member)ois.readObject();
// readObject() : 직렬화된 객체 데이터를 읽어와서
// 역직렬화시켜 정상적인 객체 형태로 반환
// ois.readObject() : ClassNotFoundException, IOException 발생 가능 -> 예외처리
System.out.println(mem);
} catch (Exception e) { // 최상위 예외 클래스로 예외 한번에 처리
e.printStackTrace();
} finally {
try {
if(ois != null) ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
'Java > 기본 개념' 카테고리의 다른 글
변수(Variable) (0) | 2024.12.16 |
---|---|
프로그래밍 기초 (0) | 2024.12.16 |
컬렉션(Collection) (2) | 2024.11.17 |
스레드(Thread) (0) | 2024.11.17 |
네트워크(Network) (0) | 2024.11.17 |