ultra_dev
이것이 자바다 CH 11(예외처리) 본문
☑️ 에러 : 컴퓨터 하드웨어 고장으로 인해 응용프로그램 실행 오류가 발생하는 것
-> 개발자가 대처할 방법이 없음
☑️ 예외 : 잘못된 사용 또는 코딩으로 인한 오류
-> 발생하면 프로그램이 곧바로 종료 한다는 점에서 에러와 같지만, 예외 처리를 통해 실행 상태 계속 유지 가능
예외 발생시 예외 클래스로부터 객체 생성 -> 예외 처리시 사용
- 일반 예외 (Exception) : 컴파일러가 예외처리 코드 여부를 검사하는 예외
- 실행 예외 (Runtime Exception) : 컴파일러가 예외처리 코드 여부를 검사하지 않는 예외
- 모든 에러와 예외 클래스는 Throwable 상속받아 만들어 짐
- 예외클래스는 추가적으로java.lang.Exception 클래스 상속 받음
- 실행 예외는 RuntimeException과 그 자식 클래스고 그 밖의 예외 클래스는 전부 일반 예외이다.
☑️ try - catch - finally 블록
try {
예외발생코드
}catch(예외클래스 e) {
예외처리
} finally {
항상 실행
}
try 블록에서 작성한 코드가 예외 없이 정상 실행시 바로 finally 블록으로 감
try 블록에서 예외 발생시 catch 블록이 실행되고 fianlly 블록으로 이동
-> finally 블록은 예외 발생 여부와 상관없이 무조건 실행
-> 심지어 try,catch블록에서 return문(메소드 종료) 사용 하더라도 항상 실행됨
☑️NullPointerException
: 참조변수가 Null인 상태에서 필드나 메소드에 접근할 경우 발생
-> 실행 예외이므로 컴파일할 때 예외 처리 코드가 없어도 되지만, 발생시 프로그램 즉시 종료
예외 처리시 :
public class Exception1
public static void printLength(String data){
try{
int result = data.length();
System.out.println(result);
}catch(NullpointerException e){ //예외 정보를 얻는 3가지 방법
System.out.println(e.getMessage()); ->1번 선택지
//System.out.println(e.toString); -> 2번 선택지
//e.printStackTrace(); -> 3번 선택지
}finally{
System.out.println("마무리");
catch라인에 써져있는 3개의 선택지는 예외 정보를 출력하는 3가지 방법임
예외가 발생하면 예외 객체가 catch 선언부의 예외 클래스 변수에 대입되는데 이후
- e.getMessage() : 예외가 발생한 이유만 리턴
- e.toString() : 예외의 종류도 리턴
- e.printStackTrace() : 예외가 어디서 발생했는지 추적한 내용까지 리턴
☑️ 다중 catch
:try 블록에 다양한 종류의 예외 발생시, 다중 catch를 사용해서 발생하는 예외에 따라 예외 처리 코드를 다르게 작성 가능
try{
ArrayIndexOutOfBoundsException 발생
NumberForamatException 발생
} catch(ArrayIndexOutOfBoundsException e){
예외처리1
} catch(NumberFormatException e){
예외처리2
}
catch 블록이 여러개라 할지라도 단 1개의 catch 블록이 실행됨
why? try블록에서 동시다발적으로 예외가 발생하지 않고, 예외 발생시 즉시 해당 catch 블록으로 이동하기 때문
💥 처리해야할 예외 클래스들이 상속 관계에 있을 때는 하위 클래스 catch 블록을 먼저 작성하고 상위클래스 catch 블록을 나중에 작성해야 한다!!!!
why? 예외 발생시 , catch블록은 위에서부터 차례대로 검사 대상이 되는데 하위 예외도 상위 클래스 타입이니
상위 클래스 catch 블록이 먼저 검사대상이 되면 안됨
ex)
try{
ArrayIndexOutOfBoundsException 발생
NumberForamatException 발생
} catch(Exception e){
예외처리1
} catch(NumberFormatException e){ <- 실행 안됨!!!!!!
예외처리2
}
이 경우 두가지 예외 모두 첫번째 catch문으로 가버림
☑️ 2개 이상의 예외를 하나의 catch 블록으로 예외 처리하고 싶을 땐 예외 클래스를 기호 | 로 연결하면 된다.
-> 2가지 이상의 예외를 동일하게 처리
.
.
.
} catch (NullPointerException | NumberForamtException e) {
.
.
}
☑️ 리소스 자동 닫기
:리소스 = 데이터를 제공하는 객체
사용할땐 열고(open) 사용이 끝나면 닫아야함(close)
리소스를 사용하다 예외가 발생해도 닫는 것이 중요. 그렇지 않으면 리소스가 불안정한 상태로 남아있게 됨
FileInputStream fis = null;
try {
fis = new FileInputStream("file.txt"); -> 파일 열기
...
}catch(IOException e) {
...
}finally{
fis.close(); -> 파일닫기
}
예외 발생 여부와 상관 없이 닫아줘야하니 finally 블록에서 close해줘야 안전
만약 try 부분에만 close하면 예외 발생시 안 닫아지니 catch부분에도 close 써줘야하고
그러면 이상하고 비효율적이기 때문에 finally 부분에 써주는 것
일일히 이렇게 닫아주는 게 귀찮으니 자동 닫기 방법이 있다.
try-with-resource 블록을 사용하면 된다.
try (리소스 여는 코드)
이렇게 try 괄호에 리소스를 여는 코드 작성시 자동으로 리소스의 close()블록 호출
try(FileInputStream fis = new FileInputStream("file.txt")) <- 이런 식으로 소괄호 안에 넣기 {
...
}catch(IOException e){
..
}
이를 위해선 필수 조건이 있음
리소스는 jaba.lang.AutoCloseable 인터페이스를 구현해서 AutoCloseable 인터페이스의 Close() 메소드를 재정의 해야함
public class FileInputStream implements AutoCloseable {
@Override
public void close() throws Exception {...}
}
만약 복수개의 리소스 사용한다면 try() 괄호 안에 세미콜론(;)으로 구분해서 작성하면 된다.
try(
FileInputStream fis1 = new FileInputStream("file1.txt"); <- ;
FileInputStream fis2 = new FileInputStream("file2.txt")
){
...
}catch(IOException e){
...
}
// AutoCloseable ac1 = 위의 리소스객체
// AutoCloseable ac2 = 위의 리소스객체2
// ac1.close();
// ac2.close();
-> 자동 리소스 닫기시 위의 4줄이 자동으로 들어가서 실행된 뒤에 생략되는 것임
-> 위에서 AutoCloseable 인터페이스를 구현하는 조건이 필요한 이유!
java9 이후 부터는
FileInputStream fis1 = new FileInputStream("file1.txt");
FileInputStream fis2 = new FileInputStream("file2.txt");
try(fis1; fis2) {
...
}catch(IOException e){
...
}
이렇게 try괄호 안이 아니라 외부에 선언 후 리소스 변수를 사용하느 것도 가능하다!
☑️ 예외 떠넘기기(throws)
:메소드 내부에서 예외 처리를 할 수도 있지만, 메소드를 호출한 곳으로 예외를 떠넘길 수 있다.
throws를 활용. 메소드 선언부 끝에다가 떠넘길 예외 클래스를 쉼표로 구분해서 나열해주면 된다.
리턴타입 메소드명(매개변수,...) throws 예외클래스1, 예외클래스2, ... {
}
throws로 메소드에서 예외처리를 하지 않고 떠넘겼기 때문에 호출하는 곳에서 예외를 받아 처리해야한다.
public void method2() throws ClassNotFoundException { -> throws로 호출한 곳으로 예외처리 던짐
Class.forName("java.lang.String2");
}
public void method1() {
try{
method2(); //method2() 호출했으니 여기서 예외처리 해줘야함
}catch(ClassNotFoundException e) {
System.out.prinln(e.getMessage());
}
}
나열해야할 예외 클래스가 많으면 throws Exception 또는 throws Throwable만으로 모든 예외 떠넘기기 가능 (상위클래스..)
main() 메소드에서도 throws 키워드를 이용해 떠넘길 수 있는데 그렇다면 JVM이 예외처리를 해줌
-> 예외의 내용을 콘솔에 출력하는 걸로 처리
☑️ 사용자 정의 예외
:표준 라이브러리에 존재하지 않는 예외들을 직접 예외 클래스 정의 후 사용 가능하다.
사용자 정의 예외는 일반예외(컴파일러가 체크)도 가능하고 실행 예외(컴파일러가 체크 안함)도 가능하다.
보통 일반 예외는 Exception의 자식 클래스로 선언하고 실행 예외는 RuntimeException의 자식 클래스로 선언한다.
public class 내마음대로Exception extends Exception 또는 RuntimeException {
public 내마음대로Exception() { //기본 생성자
}
public 내마음대로Exception(String Message) { //예외 메시지를 입력 받는 생성자
super(message);
}
}
사용자 정의 예외 클래스에는 기본 생성자와 예외 메시지를 입력받는 생성자를 선언
예외 메시지는 부모 생성자 매개값으로 넘겨주는데, 예외 객체의 공통 메소드인 getMessage()의 리턴값으로 사용하기 때문
-> catch에서 메시지 넣기 가능해짐
☑️ 예외 발생 시키기
:자바에서 제공하는 표준 예외 뿐 아니라 사용자 정의 예외를 직접 발생 시키려면 throw 키워드와 함께 예외 객체를 제공하면 된다.
예외의 원인 메시지를 제공하려면 생성자 매개값으로 전달한다.
💥앞서 나온 throws는 예외를 떠넘기는 거고, 지금 나온 throw는 예외를 발생시키는 키워드로 둘은 전혀 다른 키워드다.
throw new Exception() 또는
throw new Exception("예외메시지") 이런식으로 사용
1번 : 직접 메소드 내부에서 처리
void method(){
try{
...
throw new Exception("예외메시지")
...
}catch(Exception e) {
String message = e.getMessage();
}
}
2번 : throws로 떠넘기고 호출한 곳에서 처리
void method() throws Exception {
...
throw new Exception("예외메시지")
...
}
통상적으로 2번 방법을 사용한다.
Optional 클래스 : NPE를 방지할 수 있도록 도와준다.
Optional<T>는 null이 올 수 있는 값을 감싸는 Wrapper 클래스
❓궁금증
Optional 클래스의 .orElseThrow 메소드 사용시 try catch finally로 바로 잡든가 아니면 throws로 안던져줘도 됨..?
'이것이 자바다' 카테고리의 다른 글
이것이 자바다 CH13 제네릭 (0) | 2023.01.27 |
---|---|
이것이 자바다 CH12.모듈 (0) | 2023.01.27 |
이것이 자바다 CH 9(중첩 선언과 익명 객체) (0) | 2023.01.13 |
이것이 자바다 CH 8(인터페이스) (0) | 2023.01.13 |
이것이 자바다 CH 7-2(상속) (0) | 2023.01.13 |