🦁멋쟁이사자처럼 백엔드 부트캠프 13기 🦁
TIL 회고 - [17]일차
🚀17일차에는 입력과 출력을 담당하는 Java IO에 대해서 배웠다.
BufferedReader와 Scanner는 자주 사용해봤지만 BufferedReader사용 시 왜 예외처리를 해주어야하는지,
사용 이후에는 close()를 통해 닫아주어야하는 이유 등에 대해 생각해보지 못했다.
또 다양한 Stream객체가 있다는 것, 각자 다른 일들을 수행한다는 것 등 이번 회고를 통해 몰랐던 부분들을 정리할 수 있게되었다.
Java IO
- 자바에서 입출력 담당
- 텍스트문서 읽기, 인터넷에서 데이터 다운로드, 네트워크를 통한 데이터 전송 등
- Input과 Output으로 입력과 출력을 담당하는데 이들은 “데이터가 이동할 통로”를 가짐
이 통로는 “데이터의 흐름” = “Stream 스트림 (데이터의 흐름을 추상화)" - 스트림의 데이터타입은 같은 형태로 추상화되어있기때문에 String, int, 동영상, 이미지 등 어떤 것이 들어와도
데이터타입에 상관없이 스트림으로 받아와 처리 - 입력담당하는 스트림은 InputStream : 프로그램으로 데이터가 들어옴
- 출력담당하는 스트림은 OutputStream : 프로그램을 기점으로 밖으로 흘러나감
- Java IO는 입력을 위한 통로, 출력을 위한 통로를 “따로” 관리 (=같이 데이터가 흐르지 않는다.)
Java IO의 설계철학
- OCP(Open-Closed Principle) : 클래는 확장에는 열려 있어야 하지만 변경에는 닫혀 있어야 한다.
- 유연성 : 다양한 유형의 데이터 소스와 대상을 지원할 수 있도록 설계
이는 프로그래머가 동일한 인터페이스를 사용하여 파일, 네트워크 연결, 메모리 내 데이터 등 데이터 읽고 쓰기 가능 - 확장성 : 자바 IO는 쉽게 확장 가능, 새로운 유형의 데이터 소스, 사용자 정의 입출력을 가능하게함
- 재사용가능성 : 일관된 입출력 관련 클래스 설계로 코드 재사용 가능
(ex. 데코레이터 패턴에서는 목적지/근원지를 가지는 주인공 클래스와 “기능을 가진 클래스”인 장식 클래스가 있다.) 이 장식 클래스만 갈아끼우는 방식으로 재사용이 가능 - 주인공 클래스 : 데이터를 어디에 쓸지, 어디로부터 데이터를 읽어올 것인지의 “목적지/근원지”를 가짐
- 장식 클래스 : 주인공 클래스를 꾸며줄, 편리하게 해줄 기능을 가짐
Java IO와 Decorator 패턴
- Java.io는 데코레이터 패턴으로 만들어져있음.
ex. 파일에서 데이터를 읽어오는 스트림(Stream)에 기능을 더하는 데코레이터(Decorator)를 사용 - 한 객체를 여러 개의 데코레이터로 감쌀 수 있다.
데코레이터 사용으로 서브클래스를 만드는 것보다 유연하게 기능을 확장
Java IO의 4가지 추상클래스
- 자바 IO API의 클래스들 중 InputStream, OutputStream, Reader, Writer 등이 붙은 클래스들이 추상클래스
- 자바 초기 Byte단위의 데이터를 다루는 InputStream, OutputStream만 존재
➡️ 초기에는 1바이트만 데이터를 처리하면 됐기때문에 이러한 클래스들이 사용됨 - 하지만 한국어/중국어/일본어 등의 다양한 외국어를 처리하기 위해서는
Byte단위로 처리하는 InputStream, OutputStream에서는 Character단위로 묶어서 사용자에게 보여줄때 문자가 깨지게됨
➡️이를 해결하기위해 등장한 추상클래스 Reader(읽기), Writer(쓰기) - 이 추상클래스들은 Byte단위의 입출력을 다루는 InputStream, OutputStream 클래스와 달리
Character 단위의 입출력을 다루는 Reader, Writer 클래스
BufferedInputStream API
- "주인공 클래스"와 "장식 클래스"를 구분짓는 쉬운 방법은 해당 클래스의 생성자 부분을 확인하면 된다.
- 예를 들어 BufferedInputStream은 Buffer라는 “기능”을 갖는다.
→ 한 줄씩 입출력할때 “한 줄의 데이터”를 갖고 있을 공간을 Buffer가 제공해줌
→ read(), readLine() 등의 메소드도 제공 - ⭐따라서 Buffered가 붙어있는 클래스들은 “장식클래스"
ByteArrayInputStream API
- 4가지의 추상클래스를 생성자로 받고 있지 않다.
- 혼자 쓰일 수 있는 "주인공 클래스"인 것
CharArrayInputStream API
- 4가지의 추상클래스를 생성자로 받고 있지 않으므로 "주인공 클래스"
DataInputStream API
- InputStream타입의 in을 매개변수로 받으므로 “장식클래스"
- 이는 반드시 IO객체가 들어와서 사용되어야한다는 것
FileInputStream API
- 생성자로 File이라는 객체를 가지는 “주인공 클래스"
❓주인공 클래스가 하는 일
- 어디에서 읽어 들일 것인가, 어디에다 쓸 것인가를 결정
- FileInputStream – 파일로”부터” 읽어들인다.
- ByteArrayOutputStream – “바이트배열에” 저장한다.
- 주인공클래스는 간단한 메소드만 가지고 있다.
❓장식 클래스가 하는 일
- 주인공 클래스가 사용할 유용한 기능을 제공한다.
- DataInputStream은 readDouble(), readFloat(), readBoolean(), readInt() 등의 메소드를 제공하고 있다.
- 이처럼 장식 클래스는 다양한 메소드를 가짐
InputStream 관련 클래스
- FileInputStream : 파일 시스템에 저장된 데이터를 읽음
- ByteArrayInputStream : 메모리에 저장된 바이트 배열로부터 데이터를 읽음
- BufferedInputStream : 버퍼링을 사용하여 입력의 효율을 높임
→ 데이터를 일정량 모아두었다가 한번에 처리 (여러번 연산보다 효율적) - DataInputStream : 기본 데이터 타입(int, long, float 등)을 “바이트 스트림”으로부터 직접 읽을 수 있게함
OutputStream 관련 클래스
- FileOutputStream : 파일 시스템에 “바이트 단위로”로 데이터를 씀
- ByteArrayOutputStream : 바이트 배열에 데이터를 씀
- BufferedOutputStream : 버퍼링을 사용하여 출력 효율을 높임
- DataOutputStream : 기본 데이터 타입을 “바이트 스트림”에 쉽게 쓸 수 있게함
▶️실습 - BufferedReader
- BufferedReader의 매개변수로 Reader타입이 들어와야한다고 표시
- 이를 해결하기 위해 InputStreamReader를 사용해야함
- 이 클래스는 Reader이지만 생성자에가보면 InputStream을 받고 있음
- InputStreamReader : C타입 변환기, 젠더 등의 역할을 하는 클래스
InputStream → InputStreamReader → Reader
- 이러한 구조에서 가운데 다리 역할, 즉 젠더의 역할을 하는 것
- 젠더 역할의 InputStreamReader를 넣어주면
이번에는 이 InputStreamReader가 InputStream타입을 매개변수로 가져야한다고 표시 - 데이터를 입력을 위해 Scanner클래스에서도 사용했던 System.in을 사용
System.in
- System.in : 키보드와 연결된 System 클래스의 InputSteam 타입의 static final 상수를 의미
- in의 설명을 자세히 보면 “표준” 입력 스트림이고, 이미 열려있으며, 입력데이터를 “제공할 준비가 되어있다”고 한다.
- 따라서 System클래스의 in 필드를 직접 객체로 참조하여 넣을 수 있다. → System.in
// 1번방법. 한 줄 입력받기
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// 2번방법. 한 줄 입력받기
InputStream is = System.in;
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
- 두 방법 모두 같은 기능을 수행
- 2번 방법처럼 각 클래스들을 선언하고 매개변수로 넣어주었던 부분을
- 1번 방법처럼 간편하게 사용할 수 있는 것
❓BufferedReader의 예외처리
➡️readLine() 메소드를 사용할때 Exception처리를 해주어야한다는 에러가 발생하는데 throws로 Exception을 넘긴다.
InputStream==null 일때를 대비해 미리 예외를 처리하는 것이다.
public static void main(String[] args) throws Exception{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str = br.readLine();
}
- 이렇게 throws로 Exception을 JVM에게 예외를 넘긴다.
▶️실습 - 파일로부터 입력받기
- BufferedReader를 통해 파일로부터 “한줄씩 입력받기”를 구현 → 키보드가 근원지
- FileInputStream를 통해 “파일로부터”를 구현
- FileInputStream생성자는 IO가 아닌 File 등으로 입력받고 있기때문에 “주인공 클래스"
- 따라서 자체적으로 생성이 가능
FileInputStream fis = new FileInputStream("src/day12/IOExam01.java");
- 이처럼 BufferedReader에서는 new InputStreamReader를 통해 꾸며주어서 동작을 했다면
- FileInputStream은 자체적으로 생성자를 가지고, 파일 경로명을 받고 있는 것을 확인가능
// 1번방법. 파일로부터 한 줄 입력받기
FileInputStream fis = new FileInputStream("src/day12.io/IOExam01.java");
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br2 = new BufferedReader(isr);
// 2번방법. 파일로부터 한 줄 입력받기
FileInputStream fis2= new FileInputStream("src/day12.io/IOExam01.java");
BufferedReader br3 = new BufferedReader(new InputStreamReader(fis));
- 두 방법 모두 같은 기능을 수행
- ➡️ BufferedReader는 Reader만 받아들일 수 있으므로
FileInputStream이 직접 들어가지 못함 따라서 InputStream을 Reader로 바꿔주는 “커넥트 = 젠더”가 필요 - InputStreamReader 클래스가 "젠더 역할"을 함
▶️실습 - Hello.txt 파일을 읽어오기
// HelloIO.txt 읽어오기
FileInputStream fis3= new FileInputStream("src/HelloIO.txt");
BufferedReader br4 = new BufferedReader(new InputStreamReader(fis3));
System.out.println("데이터를 읽어왔습니다 - " + br4.readLine()); // 1줄 읽어옴
System.out.println("데이터를 읽어왔습니다 - " + br4.readLine()); // 1줄 읽어옴
▶️실습 - 키보드로부터 5줄 입력받아서 콘솔에 출력
public static void main(String[] args) throws Exception{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] strArr = new String[5];
for(int i = 0; i < strArr.length; i++){
System.out.print("5명 중 한 학생의 이름을 입력해주세요 : ");
strArr[i] = br.readLine();
}
System.out.println(Arrays.toString(strArr));
}
▶️실습 - 키보드로부터 입력받은 데이터를 “파일에 출력"
- FileWriter만 사용
- 👀파일을 열면 반드시 닫아주어야한다. ➡️close()
- 닫아주지 않으면 파일에는 아무것도 써지지 않는다.
- 운영체제에서는 버퍼를 데이터로 가득 채워야 데이터를 전달하러간다.
데이터를 하나 받고 바로 전달하고 하나 받고 바로 전달하는 것은 비효율적이기때문이다. - 따라서 데이터가 가득 찰때까지 운영체제는 기다리는데 close()를 해주지 않고
데이터도 덜 채워진 상태이면 데이터를 전달해주지 않기때문에 남아있는 공간에 상관없이 - 운영체제에게 버퍼에 데이터를 전달해달라는 명령의 동작을하는 것이 close()
public static void main(String[] args) throws Exception{
FileWriter fw = new FileWriter("src/sample/FileWriterTest.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("파일에 쓸 내용을 입력하세요.");
for(int i = 0; i < 5; i++){
System.out.print("[" + (i+1) + "줄] : ");
fw.write(br.readLine() + "\\n");
}
br.close();
fw.close();
}
- FileWriter클래스만 사용하여 실습해보았다.
PrintWriter
- FileWriter클래스와 달리 동일한 출력을 하지만 편리한 기능을 제공하는 클래스이다.
- 처음에는 "장식 클래스"로 등장했지만 자주 쓰이다보니 "주인공 클래스"로도 사용할 수 있도록 되어있다.
public static void main(String[] args) throws Exception{
FileWriter fw = new FileWriter("src/sample/FileWriterTest.txt");
PrintWriter pw = new PrintWriter(fw);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("파일에 쓸 내용을 입력하세요.");
for(int i = 0; i < 5; i++){
System.out.print("[" + (i+1) + "줄] : ");
pw.println(br.readLine());
}
br.close();
fw.close();
pw.close();
}
- FileWriter 실습 코드에서는 데이터를 입력할때마다 “\n” 줄바꿈을 명시적으로 지정해주었었는데
- PrintWriter는 출력의 기능을 잘 구현하고 있어 println()메소드로 인해 자동으로 줄바꿈이 되는 것을 확인 가능
▶️실습 - close() ➡️ try-with-resources 사용
public static void main(String[] args) {
try(
FileWriter fw = new FileWriter("src/sample/FileWriterTest.txt");
PrintWriter pw = new PrintWriter(fw);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
){
System.out.println("파일에 쓸 내용을 입력하세요.");
for(int i = 0; i < 5; i++){
System.out.print("[" + (i+1) + "줄] : ");
pw.println(br.readLine());
}
}catch(Exception e){
e.printStackTrace();
}
}
- 자동 리소스 닫기 기능을 이용하여 throws Exception을 넘겨주지 않고
- try() 블록 안에 닫을 객체들을 담아두고, { 실행할 문장 }과 catch로 Exception을 작성하면
- close()로 닫아주지 않아도 try-with-resources로 대체가능
▶️실습 - 파일로부터 입력받아서 파일에서 출력하기
public static void main(String[] args) throws Exception {
try(
FileInputStream fis = new FileInputStream("src/sample/FileInputStream.txt");
FileOutputStream fos = new FileOutputStream("src/sample/FileOutputStream.txt");
){
int readData = 0;
while((readData = fis.read()) != -1){ // EOF : End Of File 파일이 끝나면 -1값을 리턴하게됨.
fos.write(readData);
}
if(readData == -1){
System.out.println("모든 데이터를 옮겼습니다!");
}
}catch(Exception e){
throw new RuntimeException(e);
}
}
- FileInputStream으로 만들어진 파일로부터 데이터를 읽음
- 모든 데이터가 옮겨졌으면, readData는 -1으로 갱신되어있기때문에 조건문을 통해 출력을 하게된다.
- read() : int형을 반환하기때문에 (값이 없으면 -1) readData 변수를 만들어
그 변수를 통해 FileInputStream으로부터 읽어온 값이 있으면 - FileOutputStream의 write() 메소드로 그 값을 쓴다.
→ readData 변수는 “각 문자의 코드값”이 쓰여진다. (ASCII코드값) - char타입으로 형변환해보면 “문자”들이 쓰여있는것을 확인할 수도 있다.
- 값을 잘 읽어들여서 FileOutputStream.txt 에 써진 것을 확인 가능
❓FileInputStream 예외처리 해주는 이유
- API를 보면 throws FileNotFoundException처럼 예외를 넘기고 있다.
- 따라서 예외처리를 구현 → try-with-resources 로 구현가능
버퍼 Buffer
byte[] buffer = new byte[1024];
- 파일의 크기만큼 버퍼를 생성해도 운영체제가 담을 수 있는 버퍼의 공간은 한정되어있다.
따라서 (512, 1024, … 등)의 크기를 지정해야함
❓버퍼를 사용하는 이유
➡️하드디스크에 자주 접근하는 것을 방지하여 속도를 향상시킴
❓512, 1024 등의 숫자를 사용하는 이유
➡️컴퓨터는 계산할때 2진수를 사용하는데 1바이트는 8비트로 10진수로 변환하면 "256"이 된다.
이렇게 컴퓨터는 2진수로 계산하는 것이 빠르기때문에 모든 숫자를 2진수로 관리한다.
➡️ 1KB = 1024바이트이다. 1KB(킬로바이트)를 1000바이트가 아닌 2의 10제곱인 1024바이트로 사용하기때문에
컴퓨터가 1024바이트의 공간을 사용할 수 있음에도 1000바이트만 선언하게된다면
나머지 24바이트의 공간이 낭비되므로 1024를 사용하여 속도를 조금 더 빠르게 개선하는 것
▶️ 실습 - 버퍼 사용
public static void main(String[] args) throws Exception {
try(
FileInputStream fis = new FileInputStream("src/sample/fis_Buffer.txt");
FileOutputStream fos = new FileOutputStream("src/sample/fos_Buffer.txt");
){
byte[] buffer = new byte[1024];
int readData = 0;
int bufferIndex = 0;
while((readData = fis.read(buffer)) != -1){ // EOF : End Of File 파일이 끝나면 -1값을 리턴하게됨.
fos.write(buffer, 0, readData); // 버퍼를 쓸때 0번부터 시작해서 readData 까지 읽는다.
bufferIndex++;
}
System.out.println("[buffer] 데이터를 읽었습니다. : 버퍼의 크기 [" + bufferIndex + "]");
if(readData == -1){
System.out.println("모든 데이터를 옮겼습니다!");
}
}catch(Exception e){
throw new RuntimeException(e);
}
}
- 문자를 한 글자씩 옮기는 것보다 버퍼로 옮겨서 하는것이 “속도 개선”에 도움
- fos.write(buffer, off, len) : FileOutputStream의 write()를 사용하여
byte[]배열의 버퍼와 시작시점, 데이터의 길이를 매개변수로 넣어준다. - readData는 "-1"이 아닌 이상 데이터들을 받아와서 그 길이만큼 버퍼 배열에 쓰게 된다.
- fis_Buffer.txt → fos_Buffer.txt 로 잘 옮겨진 것을 확인할 수 있다.
FileInputStream fis = new FileInputStream("src/day12/io/IOExam01.java");
FileOutputStream fos = new FileOutputStream("src/sample/fos_Exam01Buffer.txt");
- 버퍼의 크기가 잘 출력되는지 확인하기 위해 FileInputStream의 경로를 IOExam01.java 파일로 바꾼다.
- 이처럼 java파일의 코드들이 txt파일로 잘 옮겨진 것을 확인할 수 있다.
문자 기반 스트림 Character
- 문자(char) 단위 처리 : 어플리케이션 주로 사용
- 인코딩 호환성 : 다양한 문자 인코딩 지원
- 텍스트 중심 : 텍스트 파일, 문자열 데이터 처리에 적합 → HTML, XML, JSON파일 처리 등
- Reader, Writer클래스
Reader 클래스
- FileReader : 파일로부터 텍스트 데이터 읽기
- StringReader : 문자열로부터 텍스트 읽기
- BufferedReader: 버퍼링을 통해 효율적인 읽기 지원
- InputStreamReader : byte스트림을 문자 스트림으로 변환하는 “젠더 / 다리” 역할
Writer 클래스
- FileWirter : 파일에 텍스트 데이터 쓰기
- StringWriter : 문자열에 텍스트 데이터 쓰기
- BufferedWriter : 버퍼링을 통해 효율적인 쓰기 지원
- OutputStreamWriter : 문자 스트림을 byte스트림으로 변환하는 “젠더 / 다리” 역할
특수 IO
- System.in : 표준 입력 스트림, 콘솔로 사용자 입력받기, InputStream형태로 제공
- System.out : 표준 출력 스트림, 콘솔에 데이터 출력, PrintStream클래스의 인스턴스
- System.err : 표준 오류 출력 스트림, 콘솔에 오류/경고 출력, PrintStream클래스의 인스턴스
- System클래스에서 모두 표준 입출력 및 오류 출력을 다루는 정적멤버들 → 콘솔 기반 입출력 작업에 사용
보조 스트림 Decorative Streams
- 기본 스트림에 추가적인 기능을 제공
- 데이터를 필터링하거나 버퍼링, 문자와 바이트간 변환, 객체 직렬화 등 다양한 기능 수행
Buffered Streams (버퍼링 스트림)
- BufferedReader, BufferedWriter : 내부적으로 버퍼를 사용해 입출력 효율을 높임
- BufferedInputStream, BufferedOutputStream
Data Streams (데이터 스트림) - DataInputStream, DataOutputStream
Object Streams (객체 스트림)
- ObjectInputStream, ObjectOutputStream
- 객체 직렬화와 역직렬화를 위해 사용
- 객체 직렬화/역직렬화 : 객체 → 바이트 형태 변환 혹은 바이트 → 객체 형태로 복원 가능
Print Streams (프린트 스트림) - PrintWriter, PrintStream
Character Streams to Byte Streams
- 문자 스트림과 바이트 스트림 변환
- InputStreamReader와 OutputStreamWriter 바이트 스트림과 문자 스트림 간의 변환을 위한 “젠더” 역할
▶️실습 - DataOutputStream (쓰기)
- 컴퓨터가 이해하고, 사용자가 이해할 수 없는 문자로 쓰여진다.
public static void main(String[] args) {
// 기본 데이터 타입으로 파일에 쓰기
// 사용자를 위한 목적이 아닌 컴퓨터를 위한 목적으로 쓰여짐 (=사용자가 이해할 수 없는 문자로 쓰여짐)
try(
DataOutputStream dos = new DataOutputStream(new FileOutputStream("src/sample/DataExam.txt"));
){
// 기본 데이터 타입을 쓰기 위한 샘플 데이터
dos.writeBoolean(true);
dos.writeByte(Byte.MAX_VALUE);
dos.writeChar('a');
dos.writeDouble(3.82);
System.out.println("데이터가 쓰여졌습니다!");
}catch(Exception e){
throw new RuntimeException(e);
}
}
▶️실습 - DataInputStream (읽기)
public static void main(String[] args) {
// 기본 데이터 타입으로 파일을 읽기
// 사용자를 위한 목적이 아닌 컴퓨터를 위한 목적으로 읽어봄 (=사용자가 이해할 수 없는 문자)
try(
DataInputStream dis = new DataInputStream(new FileInputStream("src/sample/DataExam.txt"));
){
// 기본 데이터 타입을 읽기 위한 샘플 데이터
if(dis.readBoolean()){
System.out.println("true입니다.");
}
// 꺼낼때는 DataOutputStream에서 저장한 순서대로 꺼내야함
byte b = dis.readByte();
char c = dis.readChar();
double d = dis.readDouble();
System.out.println("byte : " + b + ", char : " + c + ", double : " + d);
System.out.println("데이터를 모두 읽어왔습니다!");
}catch(Exception e){
throw new RuntimeException(e);
}
}
- 꺼낼때는 DataOutputStream에서 썼던 타입의 순서대로 읽어들여야한다.
▶️실습 - BufferedReader와 PrintWriter 사용
public static void main(String[] args) {
try(
BufferedReader reader = new BufferedReader(new FileReader("src/sample/input.txt"));
PrintWriter writer = new PrintWriter(new FileWriter("src/sample/output.txt"))
){
String dataLine;
while( (dataLine = reader.readLine()) != null){
writer.println(dataLine);
}
}catch(IOException e){
e.printStackTrace();
}
}
- BufferedReader를 사용해서 한 줄을 입력받게 하고, 그 매개변수로 FileReader 인스턴스를 만든다.
- PrintWriter로 출력을 편리하게 하고, 그 매개변수로 FileWriter를 받는다.
- try-with-resources를 통해 자동 리소스 닫기를 수행
- String dataLine : 입력받을 데이터가 없으면 null로 반환
↔ 이는 FileInputStream 실습에서 readData를 통해 ASCII코드를 입력받을때
-1이 데이터가 없는 것을 의미했던 것과는 조금 다르다.
File
- File클래스는 File을 추상화한 객체
public static void main(String[] args) {
File file = new File("FileTest.txt");
System.out.println(file.exists()); // 파일이 존재하는지 여부 출력
try {
if (!file.exists()) {
file.createNewFile();
System.out.println("새로운 파일이 생성되었습니다." + file.getAbsolutePath());
}
// File객체를 통해서 파일 정보를 확인할 수 있음
System.out.println("파일 이름 : " + file.getName());
System.out.println("파일 크기 : " + file.length() + "bytes");
System.out.println("읽기 가능 : " + file.canRead());
System.out.println("쓰기 가능 : " + file.canWrite());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
- 파일에 관련하여 유용한 메소드를 제공
- 두번째 실행에서는 파일이 존재하는지 여부를 통해 true를 반환
File 디렉토리 관련 메소드
System.out.println(System.getProperty("user.dir"));
- 현재 있는 디렉토리를 출력한다.
File dir = new File(".");
System.out.println(dir.isDirectory());
String[] files = dir.list();
for(String f : files){
System.out.println(f);
}
- “.” : 현재 디렉토리를 의미한다.
- 새로운 파일을 현재 디렉토리 경로로 만들어내고,
- isDirectory() : 현재있는 곳이 디렉토리인지 여부를 boolean으로 타입
- 👀이를 활용해 하위 디렉토리들의 파일 정보를 출력하는 것도 만들 수 있다.
- dir.list() : 이 디렉토리의 파일들을 String타입의 배열에 넣고 출력확인
❓이처럼 경로를 객체로 추상화 시켜서 사용하는 이유
➡️Java IO에서 인스턴스 생성 시 src 경로를 직접 쓰다보면 오타로 인한 오류가 발생할 수 있는데
이처럼 파일을 객체로 추상화시켜서 그 객체를 인자로 넣어주면 그러한 경우를 방지할 수 있음
객체직렬화
- 물체를 분해해서 원소들로 직렬화 (줄 세우기)하는 것을 의미
- 객체를 파일에 저장
- serialization, serializable ➡️ implements를 해주어야하는 경우가 있다.
- Person객체를 만들어서 활용
- ObjectInputStream의 readObject()메소드를 활용
- ObjectOutputStream의 writeObject()메소드를 사용
// 객체 선언
Person person = new Person("Isak", 29);
// 객체를 파일에 저장 - 객체 직렬화
try(
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("src/sample/person.txt"));
){
out.writeObject(person);
}catch (Exception e){
throw new RuntimeException(e);
}
System.out.println("객체 -> 파일 [직렬화] 완료!");
- 객체를 전송하고자할때는 객체 직렬화가 이루어져야함
- 따라서 객체 직렬화를 위해서는 Person객체가 implements Serializable 해야함
(=객체직렬화를 할 것이라고 선언 = “마크업 인터페이스”라고 함) - 그렇지 않은 경우 오류가 발생한다. → java.io.NotSerializableException
마크업 인터페이스를 수행한 후에 다시 실행해보면
객체 역직렬화
- 객체직렬화를 통해 객체를 파일로 직렬화해서 보냈다면
- 다시 객체역직렬화를 하여 파일로부터 객체를 가져올 수도 있다.
// 객체를 파일로부터 읽어오기 - 객체 역직렬화
Person readPerson = null;
try(
ObjectInputStream in = new ObjectInputStream(new FileInputStream("src/sample/person.txt")); // 확장자는 지정해주지 않아도된다.
){
readPerson = (Person)in.readObject(); // Person타입으로 형변환해주어야함 (Object로 저장되어있었으므로)
}catch(Exception e){
throw new RuntimeException(e);
}
System.out.println("파일 -> 객체 [역직렬화 결과] : " + readPerson);
URL 클래스
- 웹에서 입력받아서 사용하고자할때 활용
- URL이라는 객체로 추상화된 것을 활용 → java.net.URL 패키지
URL url = new URL("<https://www.youtube.com/>");
- openStream() : 웹페이지의 정보를 InputStream타입으로 얻어올 수 있다.
- throws Exception으로 예외처리를 넘긴다.
public static void main(String[] args) throws Exception {
URL url = new URL("<https://www.youtube.com/>");
// 한 줄을 읽기 위해 BufferedReader
// 웹에서 InputStream을 읽기위한 url.openStream()
BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
String webline = null;
while( (webline = br.readLine()) != null){
System.out.println(webline);
}
}
- 해당 페이지의 HTML 정보를 텍스트로 입력받아 가져온다.
- 👀이를 통해 크롤링을 구현할 수도 있을 것이다.
💡수업을 들으면서 빠르게 이해가 가지 않았던 부분이나 다시 알고 싶었던 부분은 실습코드와 IntelliJ의 실행결과를 캡처해놓고 회고를 진행하면서 이해하고 있다.
내가 이해한 흐름대로 회고를 작성하다보니 글이 길어져도 시간은 빠르게 지나가는 것 같다!
2시간으로 그 날의 모든 내용을 완벽히 습득하긴 어렵지만 나만의 스타일로 정리하다보니 이해에 큰 도움이 되는 것 같다.
Java IO파트도 이전에 개인적으로 강의를 보며 공부할때는 이해 하지 못한 부분이 꽤 많았는데 오늘 회고를 통해 Java IO 내용을 체득할 수 있었다. 🚀
'Recording > 멋쟁이사자처럼 BE 13기' 카테고리의 다른 글
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_19일차_''객체지향원칙 OOP" (3) | 2024.12.27 |
---|---|
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_18일차_''스레드 Thread" (0) | 2024.12.26 |
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_16일차_''컬렉션 프레임워크" (0) | 2024.12.23 |
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_15일차_''JDBC와 DAO/DTO" (2) | 2024.12.20 |
[멋쟁이사자처럼 부트캠프 TIL 회고] BE 13기_14일차_'DML, DDL, TCL' (0) | 2024.12.19 |