입출력을 위한 인터페이스와 클래스들
자바 IO는 크게 byte 단위 입출력과 문자 단위 입출력 클래스로 나뉜다.
byte 단위 입출력 클래스는 모두 InputStream과 OutputStream이라는 추상 클래스를 상속받아 만들어진다.
문자(char) 단위 입출력 클래스는 모두 Reader와 Writer라는 추상 클래스를 상속받아 만들어진다.
이 클래스들의 구분은 생성자를 보면 알 수 있다.
4가지 추상 클래스(InputStream, OutputStreamReader, Reader, Writer)를 받아들이는 생성자가 있다면,
다양한 입출력 방법을 제공하는 클래스이다. 4가지 클래스를 받아들이는 생성자가 없다면,
어디로부터 입력받을 것인지, 어디에 쓸 것인지를 나타내는 클래스이다.
파일로부터 입력받고 쓰기 위한 클래스
FileInputStream, FileOutputStream, FileReader, FileWriter
배열로부터 입력받고 쓰기 위한 클래스
ByteArrayInputStream, ByteArrayOutputStream, CharReader, CharWriter
해당 클래스들은 어디로부터, 어디에라는 대상을 지정할 수 있는 IO 클래스들로 이런 클래스를
장식대상 클래스라고 한다. DataInputStream, DataOutputStream같은 클래스를 보면 다양한 데이터 형을
입력받고 출력한다. PrintWriter는 다양하게 한 줄을 출력하는 pintln() 메소드를 가지고 있다.
BufferedReader는 한줄을 입력받는 readLine() 메소드를 가진다. 이런 클래스들은 다양한 방식으로 입력하고,
출력하는 기능을 제공하는 이런 클래스를 장식하는 클래스라고 한다.
데코레이터 패턴
하나의 클래스를 장식하는 것처럼 생성자에서 감싸서 새로운 기능을 계속 추가할 수 있도록
클래스를 만드는 방식을 말한다.
<Byte 단위 입출력>
read()가 byte를 리턴하면 끝을 나타내는 값(-1)을 표현할 수 없어 byte가 아닌 int를 리턴한다.
read() 함수 : 파일의 끝에 도달한 경우 -1을 리턴한다.
1) 파일로부터 1byte씩 읽고 씀 : read() 경우 읽어온 byte를 정수로 리턴한다.
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteIOExam1{
public static void main(String[] args){
// 파일로부터 읽어오기 위한 객체(변수 선언)
FileInputStream fis = null;
// 파일에 쓸 수 있게 해주는 객체(변수 선언)
FileOutputStream fos = null;
try {
// FileInputStream을 생성(값을 할당)
// FileInputStream이 파일이 없을 경우에 발생하는 익셉션을 처리하기 위한 try-catch문
fis = new FileInputStream("src/main/java/ex4/ByteIOExam1.java"); // "경로"
fos = new FileOutputStream("byte.txt"); // "텍스트 파일 이름"
// 파일을 읽어들이는 부분(-1을 넣어준 이유는 input 값이 없을 수도 있기 때문에)
int readData = -1;
// FileInputStream의 read 메소드는 1byte씩 읽을 수 있음
// read 메소드가 리턴하는 타입은 정수
// -> 정수의 4byte 중에서 마지막 byte에다가 읽어들인 1byte를 저장함
// 읽어들일 수 있는 코드가 여러 줄 있을 수 있기 때문에 반복문 사용
// 읽어들이는 객체는 FileInputStream이기 때문에 read 메소드를 사용하면
// 1byte를 읽어서 파일이 끝날 때까지 readData에 넣어준다(파일이 끝남을 뜻하는 -1)
// 즉, 더 이상 읽어들일 파일의 byte가 없다는 뜻
while((readData = fis.read())!= -1){
// 읽어들인 값을 FileOutputStream을 이용해서 readData를 써줌
fos.write(readData);
}
} catch (Exception e) {
e.printStackTrace();
// IO에 모든 객체들은 인스턴스화 하고나면 닫아줘야 함
}finally{
// close 메소드도 IO 익셉션을 발생시키기 때문에 익셉션 처리
try {
// FileOutputStream 객체를 닫아줌
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
// FileInputStream 객체를 닫아줌
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
변수 선언과 값 할당을 따로 해준 이유는 무엇인가?
만약 try 내부에 선언과 동시에 초기화가 되어 있다면 try-catch 외부에서 fis와 fos 변수를 사용할 수 없다.
따라서 외부에서 따로 선언을 해줌으로써 더 효율적으로 사용할 수 있게 만들어 준 것이다.
try-catch문에서 하나의 try문에서 fos와 fis를 사용하면 안되는 이유는 무엇인가?
fos.close() 하는 시점에 예외가 발생하면 fis는 close()할 수 없게 되기 때문이다.
2) 파일로부터 512byte씩 읽고 씀
: read(bytes[])의 경우 읽어온 바이트는 bytes[]에 저장하고 읽어온 개수를 정수로 리턴
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteIOExam2 {
public static void main(String[] args){
// 메소드 수행 시작 시간
long startTime = System.currentTimeMillis();
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("src/main/java/ex4/ByteIOExam1.java");
fos = new FileOutputStream("byte2.txt");
// 파일을 읽어들이는 수
int readCount = -1;
// 512만큼 읽어들여야하므로 배열 사용
byte[] buffer = new byte[512];
while((readCount = fis.read(buffer))!= -1){
// 512만큼 읽어들였기 때문에 buffer의 0번째부터 readCount만큼 써라
fos.write(buffer,0,readCount);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//메소드 수행 끝나는 시간
long endTime = System.currentTimeMillis();
//메소드를 수행하는데 걸린 시간
System.out.println(endTime-startTime);
}
}
1byte씩 읽는 것보다 수행 시간이 짧아진다. -> 512byte의 배수로 배열을 사용하는 것이 성능이 좋다.
★ 읽어보면 도움되는 부가적인 설명 ★
https://school.programmers.co.kr/questions/581
프로그래머스
코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.
programmers.co.kr
'강의 정리하기 > JAVA' 카테고리의 다른 글
어노테이션 (0) | 2023.03.10 |
---|---|
자바 IO(2) (1) | 2023.03.10 |
Date 클래스, Calendar 클래스, java.time 패키지 (0) | 2023.03.09 |
Generic (0) | 2023.03.09 |
기본 API 클래스(2) - 컬렉션 (0) | 2023.03.09 |