초록꼬마의 devlog
article thumbnail

2021.11.17(수)

🌿 Wrapper 클래스

  • 기본자료형(boolean, char, byte, short, int, long, float, double)을 객체로 포장해줌
  • 기본자료형을 객체로 취급해야 하는 경우
  1. 메소드 호출 시 = 기본자료형에 대해 메소드를 사용하고 싶을 때
    e.g. equals(), compareTo(),
  2. 메소드의 매개변수로 기본자료형이 아닌 객체 타입만 요구될 때
  3. 다형성을 적용시키고 싶을 때
  4. String 자료형을 기본자료형으로 parsing할 때 ← Java에서 parser는 메소드로 구현되어 있음
    • 표현법: 해당Wrapper클래스이름.parse기본자료형XXX(변환할문자열);

기본 자료형 → String 변환할 때: String.valueOf(변환할기본자료형값) → 반환형 = String
API 문서 많이/자주 읽어 숙지하기 → 업무시간 단축

🌿 Input/Output

  • 프로그램 상의 데이터를 외부매체(모니터/스피커 등 출력장치, "파일")로 출력하거나 입력장치(키보드, 마우스, 마이크, "파일")로 입력받는 과정 = IO/Input & Output/입출력
  • IO를 진행하고 싶다면 우선적으로 '반드시' 프로그램과 외부매체와의 '통로(Stream, 스트림)'를 만들어줘야 함
  • 스트림의 특징
  1. 단방향 = 입력이면 입력, 출력이면 출력 → 입/출력용 스트림 따로 존재 vs 동시에 입출력을 하고자 한다면, 하나의 스트림으로는 불가능 → 입/출력 스트림 둘 다 갖고 있어야 함
  2. 스트림은 좁음 → 선입 선출(first-in first-out/FIFO, 자료구조의 큐(queue)의 개념, 순서대로 들어가서 나옴) → 시간 지연 문제 발생 가능
  • 스트림의 구분
  1. 통로의 사이즈에 따라
    • byte 스트림: 1byte짜리가 이동할 수 있는 좁은 통로 → 입력 스트림 = XXXInputStream, 출력 스트림 = XXXOutputStream
    • 문자 스트림: 2byte(문자)짜리가 이동할 수 있는 비교적 넓은 통로 → 입력 스트림 = XXXReader, 출력 스트림 = XXXWriter
  2. 외부매체와의 직접적인 연결 여부에 따라
    • 기반 스트림: 외부매체와 직접적으로 연결되는 통로
    • 보조 스트림: 기반 스트림만으로 부족한 성능을 향상시켜주는 용도의 스트림, 기반 스트림을 만들고 추가해주는 용도인 바, 단독 사용 불가 → 속도 향상, 자료형에 맞춰서 변환, 객체 단위로 입출력하도록 도움 등

Java program(내의 데이터) → 파일 save/write = 출력
파일(내의 데이터) read → Java program = 입력

🌱 Byte 스트림

  • 1byte 단위
  • FileOutputStream, FileInputStream
  • 사용 예시

DAO(data access object): 데이터가 보관되어있는 공간에 직접 접근해서 데이터를 입출력하는 메소드들을 모아둔 클래스, model.dao 패키지에 모아둠
- 기능별로 클래스를 구분(model.vo, controller, view..)해서 필요한 것들 모아두는 이유 = 유지/보수의 용이성

public class FileByteDao { 	
	public void fileSave() {
		// FileOutputStream: '파일'로 데이터를 출력하되, 1byte 단위로 출력		
		FileOutputStream fout = null;
	
		try {
			// 1. FileOutputStream 객체 생성 = 해당 파일과 직접 연결되는 통로 생성
			// 해당 파일이 존재하지 않는 경우, 해당 파일이 생성되면서 통로도 연결됨
			// 해당 파일이 존재하는 경우, 그냥 통로만 연결됨 + 옵션(뒤에 계속 이어서 쓸지 ou 파일 덮어쓸지) 지정 가능
			// 옵션 = true를 씀(해당 파일에 내용이 있을 경우 이어서 작성) ou true를 안 씀(기본값; 내용을 덮어씀)
			fout = new FileOutputStream("a_byte.txt"/*, true*/); // 기본생성자로는 객체 생성 불가능
			
			// 2. write() 메소드 호출 -> 연결 통로로 데이터 출력
			// 1byte 자료형 (표현) 범위 = -128 ~ 127 숫자 출력 가능 -> 0 ~ 127 숫자만 사용 가능 + 음수는 사용 불가능 cf. ASCII table(코드표)
			// 데이터 출력
			// fout.write(97); // a; "Unhandled exception type IOException" -> 예외 처리 필요(자동완성으로 하기)
			// fout.write(98); // b; 윗줄 a 인자 메소드 호출 주석 처리 후, 이 줄 메소드 호출 -> a_byte.txt 파일에는 b가 입력되어있음 -> 쓸 때마다 덮어쓰게 됨 -> FileOutpuStream 생성자에 true 옵션 쓴 뒤 다시 실행해보면 'bb' 입력되어있음
			
			fout.write(97); // a
			fout.write(98); // b
			fout.write(99); // c
			fout.write(100); // d
			// e(101), f(102), g(103)를 한 번에 입력하고 싶다면, 배열 사용 -> byte형을 전달할 수 있는 스트림이므로 다음 char[] arr = {101, 102, 103}, int[] 배열 사용 불가
			byte[] arr = {101, 102, 103};
			fout.write(arr); // efg 추가; 메소드의 오버로딩 -> byte형 정수, byte형 배열 등 여러 종류의 매개변수 사용 가능
			fout.write(arr, 1, 2); // arr배열의 1번 index부터 2개만 씀 -> fg 추가
			
			fout.write('A');
			fout.write('B');
			fout.write('C'); // ASCII 코드표에 있는, 1byte짜리니까 입력 가능 -> ABC 추가
			
			fout.write('강');
			fout.write('미');
			fout.write('피'); // 한글은 2bytes이기 때문에 깨져서 저장됨(�< 추가) -> 바이트 스트림으로는 불가능 -> 문자 스트림을 사용하여 해결 가능
			
			// 3. 스트림을 다 사용했다면, 반드시/꼭 자원 반납(스트림 닫기) -> 내가 안 하면 garbage collector가 해주긴 하지만, 안 하면 추후 문제 발생 가능성이 있으므로, 하는 습관 들이기 
			// fout.close(); // 이렇게 try문 안에 써둔 경우, 위에서 예외가 발생했다면 (그 예외 처리를 위해 catch문으로) passing 되어서 실행 안 될 가능성 있음 			
		} catch (FileNotFoundException e) { // 파일을 찾을 수 없는 예외가 발생할 수 있으니 처리해줘; 이 예외는 IOException을 상속받고 있음/IOException의 한 종류
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally { // 어떤 예외가 발생했든 (catch한 뒤) 반드시 실행할 구문을 finally 블럭 안에 작성 -> 자원 반납
			try {
				fout.close(); // 이 메소드 실행 시 IOException이 발생할 수도 있다는 오류 메시지 -> try/catch문으로 감싸줌
			} catch (IOException e) {
				e.printStackTrace();
			}
		} // finally문 영역 끝		
	} // fileSave() 메소드 종료
	
	public void fileRead() {
		// FileInputStream: 파일로부터 데이터를 가져와서 입력하되, 1byte 단위로 입력받음
		FileInputStream fin = null;
		
		try {
			// 1. 객체 생성 = 스트림을 엶
			fin = new FileInputStream("a_byte.txt"); // // 연결할 것(?)을 생성자의 인자로 전달해서 객체 생성 -> 외부매체(파일)과 연결할 통로 생성
			
			// 2. read() 메소드 이용 -> 통로로 데이터 입력받기
			// 1byte 단위로 하나씩 읽어옴
			// 파일에 몇 bytes가 있는지 모르므로 (-> 반복 횟수 모르므로) while문 사용			
			int value = 0;
			while ((value = fin.read()) != -1) { // 비교연산자의 결과는 논리값
				System.out.println(value);
			}
			
			// 3. 마지막으로 해야 할 일 = close() 메소드 이용 -> 자원 반납
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				fin.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	} // fileRead() 메소드 종료
} // FileByteDao 클래스 영역 끝

 

🌱 문자 스트림

  • 2bytes 단위
  • FileWriter, FileReader

🌱 보조 스트림

기반스트림이 Input/Output 계열일 경우, 보조스트림도 Input/Output 계열 사용
기반스트림이 Reader/Writer 계열일 경우, 보조스트림도 Reader/Writer 계열 사용

  1. BufferedWriter, BufferedReader
    • 버퍼 공간 제공해서 모아뒀다가 한꺼번에 입출력 진행 → 속도 향상
    • 표현법: 보조스트림클래스이름 객체이름 = new 보조스트림클래스이름(기반스트림객체);
    • 사용 예시
public class BufferedDao {
    public void fileSave() {
        FileWriter fw = null; // 기반스트림 객체 선언; 언제/어디서 사용할지 모르니까 미리 선언만 해 둔 것임
        BufferedWriter bw = null; // 보조스트림 객체 선언

        try {
            // 1. 기반스트림 객체 생성 = 연결통로 만들기
            fw = new FileWriter("c_buffer.txt");

            // 2. 보조스트림 객체 생성
            bw = new BufferedWriter(fw);
            // bw = new BufferedWriter(new FileWriter("c_buffer.txt")); // 이렇게 1줄로 기반스트림과 보조스트림 객체 동시에 생성 가능

            // 3. write() 메소드 호출 -> 파일에 쓰기
            bw.write("안녕하세요");
            bw.newLine();
            bw.write("반갑습니다\n");
            bw.write("잘 가셔요~");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 4. close() -> 자원이 생성된 순서의 역순으로 자원 반납
            try {
                bw.close();
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    } // fileSave() 메소드 종료

    public void fileRead() throws FileNotFoundException {
        // 프로그램 <- 외부매체(파일)
        // 입력

        // FileReader 기반으로 BufferedReader 추가해서 사용		
		/*
		FileReader fr = null;
		BufferedReader br = null;
		
		try {
			// 1. 기반스트림 객체 생성
			fr = new FileReader("c_buffer.txt");
			// 2. 보조스트림 객체 생성
			br = new BufferedReader(fr);
			
			// 3. read() -> 읽어오기
			String value = null;
			while ((value = br.readLine()) != null){
				System.out.println(value);
			}			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			// 4. close() -> 자원 반납; 언제 어디서 문제가 발생할지 모르므로, 원칙에 따라 close해주는 것이 좋음
			try {
				br.close();
				fr.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		*/

        // 실수로 자원 반납을 까먹는 경우 등 누가 대신 close 해 줄 수 있을까?
        /* try ~ with ~ resource 구문
         * JDK7 버전 이상부터 가능
         * 표현법:
         * try {
         * 		예외가 발생할법한 구문
         * } catch(예외클래스이름 e) {
         * 		해당 예외가 발생했을 때 실행할 구문
         * }
         * 스트림객체(+선언 부분도 같이)를 try에 넣어버리면 스트림 객체 생성 후 해당 블록의 구문이 실행 완료되면 알아서 자원 반납이 됨
         * 객체 생성을 여러 개 할 경우 마지막 생성부는 ; 붙여도 되고 안 붙여도 됨
         */
        // close 안 써도 되는 방법
        try (BufferedReader br = new BufferedReader(new FileReader("c_buffer.txt"))) {
            // 방법1) 수업시간에 한 방법
            /*
            String value = null; // 강사님께서는 ""로 초기화하셨는데..
            while ((value = br.readLine()) != null) {
                System.out.println(value);
            }
             */

            // 방법2) 강사님께서 수업 후 올려주신 방법
            String s = br.lines().collect(Collectors.joining("\n"));
            System.out.println(s);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    } // fileRead() 메소드 종료
}

 

📗 소감

- '입/출력'의 개념에 익숙해지는 데 조금 시간이 걸렸다

- 강사님께서 수업 중간에 내신 퀴즈를 못 풀어서 속상했다.. br.readLine() 메서드 동작을 정확히 이해 못했으니까 그렇지.. 

- 이 부분은 어떻게 연습을 할 수 있을지 잘 모르겠다. 일단 "Java의 정석" 등 책 보면서 내용 더 읽어보자!

 

📗 homework: BufferedReader로 데이터 읽을 때 반복문(while((value = br.readLine()) != null)) 외에 새로운 방법 찾아보기