KOSTA 12일차) 예외처리 / 입출력 스트림

해버니·2023년 3월 2일
0

KOSTA

목록 보기
7/32
post-thumbnail

복습

컬렉션 (List, set, map)
자바 제공 api
자료구조를 미리 구현해서 제공하는 api


List(ArrayList)
제너릭 : 타입 한정
add() : 데이터 추가
get(방번호) : 데이터 추출
size() : ArrayList 크기


Map
키와 값 저장 / 순서없다 / 키로 검색 / 키는 중복 안 됨.

put(키, 값)
get(키)















예외처리

프로그램이 런타임에 중단되는 것을 예방하기 위한 코드

예외처리는 C++에서 시작이 됐다.


❓ 왜 사용할까 ❓
런타임시에 발생하는 문제를 미연에 방지하기 위해서.
안전성을 위해서 중간 중간 점검하는 것이다.




public class Ex1 {
	public static void main(String[] args) {
		System.out.println("프로그램 시작");
		int x = 3 / 0;
		System.out.println("프로그램 종료");

	}
}

//프로그램 시작
//Exception in thread "main" java.lang.ArithmeticException: / by zero
//	at 예외처리.Ex1.main(Ex1.java:7)

위 코드를 보면 프로그램이 끝까지 돌아가지 않고 중단된다.
"프로그램 종료"라고 출력되지 않았다.

프로그램을 끝까지 돌아갈 수 있게 만들기 위해서,
프로그램 종료를 하기 위해서,
프로그램의 갑작스런 비정상 종료를 막기 위해서,
예외처리를 하는 것이다.


자바에서 예외가 발생하면 자바 시스템은 예외가 발생한 프로그램에 예외 객체를 던진다
프로그램은 예외 객체를 맞으면 기본 동작이 죽는다.
프로그램이 예외 객체를 맞아서 중단되지 않고 try-catch블록으로 발생한 예외객체를 잡아 예외를 처리할 있도록 하는 방법이 예외처리다








try-catch

public class Ex1 {
	public static void main(String[] args) {
		System.out.println("프로그램 시작");
		try {
			int x = 3 / 0;
		} catch (ArithmeticException e) {
			e.printStackTrace();  // 예외메시지 상세 출력
		}
		System.out.println("프로그램 종료");
	}
}

// 프로그램 시작
// java.lang.ArithmeticException: / by zero
// 프로그램 종료
//	at 예외처리.Ex1.main(Ex1.java:7)

위처럼 try-catch를 넣어주면 프로그램이 끝까지 돌아가게 된다.






try {
	예외 발생이 예상되는 or 강제로 예외처리를 요구하는 코드 
} catch(예외객체1) {
	예외처리코드
} catch(예외객체2) {
	예외처리코드
}

여러개의 catch를 넣을 수 있다.






public class Ex1 {
	public static void main(String[] args) {
		System.out.println("프로그램 시작");
		try {
			String s = null;
			s.charAt(0);
			int x = 3 / 0;
		} catch (ArithmeticException e) {
			e.printStackTrace();
		} catch (NullPointerException e) {
			e.printStackTrace();
		}
		System.out.println("프로그램 종료");
	}
}
// 프로그램 시작
// java.lang.NullPointerException: Cannot invoke "String.charAt(int)" because "s" is null
// 	at 예외처리.Ex1.main(Ex1.java:8)
// 프로그램 종료

catch블록은 try블록에서 발생한 예외 객체와 타입이 같아야 그 예외 객체를 받아서 처리할 수 있다.
그러므로 catch 블록은 타입별로 여러 개 작성이 가능하다.





Exception e

try {
	예외 발생이 예상되는 or 강제로 예외처리를 요구하는 코드 
} catch(예외객체1) {
	예외처리코드
} catch(예외객체2) {
	예외처리코드
} catch(Exception e) { 
	예외처리코드
}

실수로 예외처리를 하지 않을수도 있으니 맨 마지막에 모든걸 처리해줄 수 있는 것을 만들 수 있다.


Exceptio e는 모든 예외 처리를 받는다.
순서대로 try catch를 하기 때문에 마지막에 둬야 한다.








finally 블럭

try {
	예외 발생이 예상되는 or 강제로 예외처리를 요구하는 코드 
} catch(예외객체1) {
	예외처리코드
} catch(예외객체2) {
	예외처리코드
} catch(Exception e) { 
	예외처리코드
} finally {

}

정상 실행, 예외 처리가 됐던 안 됐던 간에 상관없이 위 어느 케이스에나 무조건 실행되는 블럭이다.

finally는 사용했었던 자원을 해제할 때 사용한다.
파일을 열었다가 닫지 못하고 중단된다던지, 데이터베이스를 열고 못 닫았다던지, 그럴 때 해제를 해야 안정성이 높아진다.
(사용했었던 자원을 해제하기 위해서 넣어준다.)




public class 예외처리기본 {
	public static void main(String[] args) {
		int[] arr = { 1, 2, 3 };
		String s = null;
		try {
			System.out.println("실행1");
			for (int i = 0; i < arr.length + 1; i++) {
				System.out.println(arr[i]);
			}
			System.out.println("실행2");
			s.charAt(0);
			System.out.println("실행3");
		} catch (NullPointerException e) {
			System.out.println(e);
		} catch (ArrayIndexOutOfBoundsException e) {
			System.out.println(e);
		} catch (Exception e) {
			System.out.println(e);
		} finally {
			System.out.println("무조건 실행되는 finally 블록");
		}
	}
}

// 실행1
// 1
// 2
// 3
// java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
// 무조건 실행되는 finally 블록









throws

예외처리 미루기
이 메서드를 호출하는 쪽에서 예외처리를 하도록 미룬다.


그래서 예외처리에는 두 가지의 종류가 있다.

trycatch로 하던가
throws로 미루던가





import java.io.IOException;

class Test {
	public void f1() throws IOException {
		int ch = System.in.read();
	}
}

public class 예외처리미루기 {
	public static void main(String[] args) {
		Test t = new Test();
		try {
			t.f1();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
}

IOException 예외 처리를 미룬다.
이 메서드를 호출하는 쪽으로 미룬다.
호출하는 쪽에서는 try catch로 해주면 좋다. (미루는 것 보다)








throw

예외 발생시킴 / 예외 객체 직접 던짐
예외를 강제로 발생시킨 후, 상위 블럭이나 catch문으로 예외를 던진다.


throw 예외객체;
class Test2 {
	private int[] datas;
	private int cnt;

	public Test2() {
		datas = new int[3];
	}

	public void add(int x) throws Exception {
		if (cnt == datas.length) {
			throw new Exception("☆ 배열이 가득 찼습니다. ☆"); // 예외 발생. 예외 객체 생성시 생성자에 파람으로 문자열을 넣으면 예외 메시지로 사용된다. 
		}
		datas[cnt++] = x;
	}

	public void printArr() {
		for (int i = 0; i < cnt; i++) {
			System.out.println(datas[i]);
		}
	}
}

public class 예외던지기 {
	public static void main(String[] args) {
		Test2 t = new Test2();

		for (int i = 0; i < 4; i++) {
			try {
				t.add(i);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		t.printArr();
	}
}

// java.lang.Exception: ☆ 배열이 가득 찼습니다. ☆
//	at 예외처리.Test2.add(예외던지기.java:13)
//	at 예외처리.예외던지기.main(예외던지기.java:31)
// 0
// 1
// 2






🔹 throw와 thwos의 차이점

throw : 에러를 고의로 발생시킬 때 사용
throws : 자신을 호출한 상위 메소드로 에러를 던지는 역할

















입출력 스트림

프로그램은 외부에서 데이터를 읽거나 외부로 데이터를 출력하는 작업이 빈번하게 일어난다.
이때 데이터는 어떠한 통로를 통해서 데이터가 이동되는데, 이 통로를 Stream이라고 한다.


stream : 줄기, 흐르다
입출력 스트림은 소프트웨어 모듈을 얘기한다.



입력 스트림 : 밖에서 프로그램 쪽으로 데이터의 흐름을 소프트웨어로 구현한 모듈
출력 스트림 : 프로그램에서 밖으로 데이터의 흐름을 소프트웨어로 구현한 모듈

입/출력은 양방향은 없고 단방향만 있다.

표준 입출력

System.in → 표준입력 System.in.read() : 키보드 입력
System.out → 표준출력 System.out.println() : 콘솔 출력
System.err → 표준에러



파일이나 네트워크로도 입력을 받아올 수 있는데 그때 입출력 스트림을 이용한다.
입출력 스트림은 굉장히 종류가 많다.
몽땅 다 알 필요는 없고 자주 사용하는 것만 알고있으면 된다.










자바스트림을 나누는 기준

바이트 스트림 : 한 바이트씩 읽고 씀

(stream으로 끝나면 한 바이트씩 입력을 받는구나.라고 생각하면 된다)




InputStream : FileInputStream (파일에서 1바이트씩 읽는 스트림)

int read() → 한 바이트 읽어서 int 타입으로 반환
int read(byte[]) → 배열의 크기만큼 : 파람으로 넣은 배열 크기만큼 읽어서 배열에 저장. 읽은 바이트 수를 반환하는 메소드이다.
int read(byte[], offs, size) → size만큼 읽어서 배열에 저장. 저장 시작 위치를 offs로. 읽은 바이트 수를 반환


byte[] buf = new byte[10];
int read(buf);
// 10바이트씩 자른다.







OutputStream : FileOuputStream (파일에서 1바이트씩 출력하는 스트림)

void write(int ch) → 한 바이트(ch) 출력
void wrtie(byte[]) → 배열 크기만큼 배열 요소들을 출력. 출력한 바이트 수 반환
void write(byte[], offs, size) → byte[] 배열에서 offs 위치부터 size 크기만큼을 출력. 출력한 바이트 수 반환
















문자 스트림 : 한 문자씩(2B) 읽고 씀

Reader : FileReader(파일에서 2바이트씩 읽는 스트림)
Writer : FileWriter(파일에서 2바이트씩 출력하는 스트림)





Reader

int read() → 2바이트씩 읽어서 int 타입으로 반환
int read(char[]) → 파람으로 넣은 배열 크기만큼 읽어서 배열에 저장. 읽은 문자 수를 반환
int read(char[], offs, size) → size만큼 읽어서 배열에 저장. 저장 시작 위치를 offs로 읽은 문자 수를 반환



import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;

public class FileReaderTest {

	public static void main(String[] args) {
		try {
			// FileReader : 파일에서 문자단위로 (2바이트씩) 읽는 스트림
			FileReader fr = new FileReader("src/입출력스트림/files/a.txt");
//			int ch;
//			while ((ch = fr.read()) != -1) {
//				System.out.print((char) ch);
//			}
			char[] bur = new char[10];
			int size = 0;
			while ((size = fr.read(bur)) > 0) {
				System.out.print(bur);
				Arrays.fill(bur, ' ');
			}
			fr.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

FileReader : 파일에서 문자단위로 (2바이트씩) 읽는 스트림






Writer

void write(int ch) → 문자 한 개 출력
void write(char[]) → 배열 크기만큼 배열 요소들을 출력. 출력한 문자 수 반환
void write(char[], offs, size) → char[] 배열에서 offs 위치부터 size 크기만큼을 출력. 출력한 문자 수 반환
write(String str) → 문자열 출력








파일 복사 프로그램 만들기

프로그램 시작시 원본 파일명, 복사본 이름 두 개 입력받아서 복사 작업완료하기.



헿.. 쌤거랑 내거랑 완전 다르다..
난 왜 이렇게 함수를 안 쓰는 거니!


내가 짠 코드

package 입출력스트림;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Scanner;

public class Copy {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		System.out.print("복사하고자 하는 파일명을 입력해주세요 : ");
		String co = sc.next();
		System.out.print("새로운 파일명을 입력해주세요 :");
		String py = sc.next();
		String str = "";

		try {
			FileReader fr = new FileReader("src/입출력스트림/files/" + co + ".txt");
			char[] bur = new char[10];
			int size = 0;
			while ((size = fr.read(bur)) > 0) {
				for (int i = 0; i < bur.length; i++) {
					str += bur[i];
				}
				Arrays.fill(bur, ' ');
			}
			fr.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		try {
			FileWriter fw = new FileWriter("src/입출력스트림/files/" + py + ".txt");
			char[] bur = new char[10];
			int size = 0;
			fw.write(str);
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}






쌤이 짠 코드

package 입출력스트림;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Scanner;

class FileCopy {

	// 파일 읽기 메서드. 파람으로 읽을 파일의 경로를 받음
	public String fileRead(String path) {
		// 문자열 조작을 빠르게 처리하는 클래스다.
		// 파일에서 읽은 내용을 저장
		StringBuilder sb = new StringBuilder();
		try {
			// 파일 읽기 스트림 생성
			FileReader fr = new FileReader(path);
			// 문자 100개씩 읽어서 담을 배열
			char[] buf = new char[100];

			while (fr.read(buf) > 0) {
				sb.append(buf); // append(): StringBuilder에 문자열 끝에 추가
				Arrays.fill(buf, ' '); // buf에 남은 쓰레기 값 삭제
			}
			fr.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return sb.toString(); // 파일 전체 내용 반환. toString() : StringBuilder 값을 String으로 변환
	}

	// 파일 복사 메서드. 파람1: 원본 파일 경로, 파람2: 복사 파일 경로
	public void copy(String src, String des) {
		// 원본 파일 내용을 읽어서 변수 txt 저장
		String txt = fileRead(src);
		try {
			// 파일 출력 스트림 생성
			FileWriter fw = new FileWriter(des);
			// txt 전체를 한 번에 출력 스트림으로 출력
			fw.write(txt);
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

public class CopyAnswer {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);

		String dir = "src/입출력스트림/files/";
		String end = ".txt";

		System.out.print("원본 파일명 : ");
		String origin = sc.next();
		
		// File : 파일에 대한 정보. 파일이 존재하나, 읽기, 쓰기, 실행 가능 등의 파일에 대한 정보를 갖는다.
		File f = new File(dir + origin + end);
		if (!f.exists()) { // exists() : 해당 파일이 존재하면 true, 아니면 false 반환
			System.out.println("원본 파일이 존재하지 않습니다.");
			return;
		}
		// 복사될 파일명 입력
		System.out.print("복사 파일명 : ");
		String copy = sc.next();
		
		FileCopy fc = new FileCopy();
		
		// 복사 메서드 실행
		fc.copy(dir + origin + end, dir + copy + end);
		
		// 복사 파일의 내용 읽음
		String res = fc.fileRead(dir + copy + end);
		
		// 파일 내용 출력
		System.out.println(res);
	}
}














스트림

1차 스트림 (기본 스트림) : 단독으로 사용 가능
2차 스트림 (보조 스트림) : 꼭 기본 스트림에 연결해서 사용해야 하고 단독으로 사용 불가능, 기본 스트림에 기능을 추가하기 위해서 사용


기본 스트림 + 보조 스트림 → 서로 타입이 맞아야 함.
읽기끼리, 쓰기용끼리 연결
바이트 단위끼리, 문자단위끼리 연결.




🍓 종류 🍓

Buffered : 속도 빠르게
FileInputStream + BufferedInputStream
FileOutputStream + BufferedOutputStream
FileReader + BufferedReader
FileWriter + BufferedWriter
바이트로 읽을 것을 문자로 변환
객체 단위 읽기
랜덤한 위치에서 읽기/쓰기







FileInputStream + BufferedInputStream & FileOutputStream + BufferedOutputStream 예제

package 입출력스트림;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class BufferedStreamTest {

	public static void main(String[] args) {
		try {
			// 기본 스트림
			FileInputStream fi = new FileInputStream("src/입출력스트림/files/a.txt");
			// 보조 스트림
			BufferedInputStream bi = new BufferedInputStream(fi);// 보조 스트림 생성자에 연결할 기본 스트림 객체 전달

			int ch;
			while ((ch = bi.read()) != -1) {
				System.out.print((char) ch);
			}
			bi.close(); // 보조 스트림 닫음
			fi.close(); // 기본 스트림 닫음

			// 기본 스트림 생성
			FileOutputStream fo = new FileOutputStream("src/입출력스트림/files/b.txt");
			// 보조 스트림 생성
			BufferedOutputStream bo = new BufferedOutputStream(fo);

			for (int i = 'a'; i <= 'u'; i++) {
				bo.write(i);
			}
			bo.close();
			fo.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}









FileReader + BufferedReader & FileWriter + BufferedWriter 예제

package 입출력스트림;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class BufferedReadAndWriter {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			FileReader fr = new FileReader("src/입출력스트림/files/a.txt");
			BufferedReader br = new BufferedReader(fr);

			int ch;
			while ((ch = fr.read()) != -1) {
				System.out.print((char) ch);
			}
			br.close();
			fr.close();

			FileWriter fw = new FileWriter("src/입출력스트림/files/b.txt");
			BufferedWriter bw = new BufferedWriter(fw);

			for (int i = 'A'; i <= 'Z'; i++) {
				bw.write(i);
			}
			bw.close();
			fw.close();

		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}



0개의 댓글